Шаг 105.
Язык программирования Go.
Пример. Вспомогательные функции

    На этом шаге продолжим рассматривать использование семейства пользовательских типов в Go на примере. Рассмотрим вспомогательные функции для организации корректной работы приложения.

    1. Определение неэкспортируемых переменных для хранения ссылок на вспомогательные функции.

/* Три неэкспортируемые переменные, каждая из которых принимает значение типа int
и возвращает значение типа int */
var saneLength, saneRadius, saneSides func(int) int
/* Для данного пакета реализована функция init(), где переменным
присваиваются ссылки на соответствующие анонимные функции */
func init() {
    saneLength = makeBoundedIntFunc(1, 4096)
    saneRadius = makeBoundedIntFunc(1, 1024)
    saneSides = makeBoundedIntFunc(3, 60)
}

    2. makeBoundedIntFunc(minimum, maximum int) func(int) int - функция, которая возвращает другую функцию, возвращающую некоторое указанное значение.

/* Функция возвращает другую функцию, возвращающую указанное значение x,
если оно находится между значениями minimum и maximum (включительно),
или ближайшее граничное значение.
   Если значение x находится за границами диапазона, функция не только возвращает
допустимое альтернативное значение, но еще и регистрирует проблему в журнале.
Однако в сообщении об обнаруженной проблеме не должно фигурировать имя функции,
созданной здесь (то есть saneLength(), saneRadius() или saneSides()), потому что
фактически проблема рождается в вызывающих их функциях. Поэтому вместо имени
функции, созданной здесь, в журнал выводится имя вызывающей функции,
которое возвращает функция caller(). */
func makeBoundedIntFunc(minimum, maximum int) func(int) int {
    return func(x int) int {
        valid := x
        switch {
        case x < minimum:
            valid = minimum
        case x > maximum:
            valid = maximum
        }
        if valid != x {
            log.Printf("%s(): изменена %d с %d\n", caller(1), x, valid)
        }
        return valid
    }
}

3. caller(steps int) string - функция возвращает строку, содержащую имя вызывающей функции.

func caller(steps int) string {
    name := "?"
    /* Функция runtime.Caller()возвращает информацию о функции, которая была
    вызвана в текущей go-подпрограмме, но еще не вернула управление.
       Ее аргумент типа int сообщает, на сколько шагов (то есть функций) назад
    следует заглянуть. При значении 0 аргумента возвращается информация о
    текущей функции (то есть о самой функции caller()), при значении 1
    возвращается информация о вызвавшей ее функции и т. д. Здесь к значению
    аргумента добавляется 1, чтобы сразу начать с вызывающей функции.
       Функция runtime.Caller() возвращает четыре значения:
       - программный счетчик (сохраняется в переменной pc),
       - имя файла, где произошел вызов (значение игнорируется),
       - номер строки, где произошел вызов (значение игнорируется),
       - логический флаг (сохраняется в переменной ok),
         сообщающий об успешной или неудачной попытке извлечения информации. */
    if pc, _, _, ok := runtime.Caller(steps + 1); ok {
        name = filepath.Base(runtime.FuncForPC(pc).Name())
    }
    return name
}
/* В случае успеха программный счетчик передается функции runtime.FuncForPC(),
возвращающей значение типа *runtime.Func, которое затем передается методу
runtime.Func.Name() для получения имени вызывающей функции.
   Возвращаемое имя имеет вид пути к файлу, например:
/Go/goproject/src/shaper1/shapes.FilledRectangle – для функции, или
/Go/goproject/src/shaper1/shapes.*shape.SetFill – для метода.
   В нашем проекте путь к файлу не представляет интереса, поэтому он отбрасывается
с помощью функции filepath.Base().
   После этого имя возвращается вызывающей программе. */

    На следующем шаге продолжим рассматривать пример использования семейства пользовательских типов в Go.


Предыдущий шаг Содержание