Идентификатор структуры с подчеркиванием перед именами функций - PullRequest
0 голосов
/ 17 мая 2018

Я работаю с Go, в частности, с привязками QT.Однако я не понимаю использование начальных подчеркиваний в структуре ниже.Мне известно об использовании подчеркивания в целом, но не об этом конкретном примере.

type CustomLabel struct {
    core.QObject

    _ func() `constructor:"init"`
    _ string `property:"text"`
}

Относится ли это к тегам структуры?

Ответы [ 2 ]

0 голосов
/ 17 мая 2018

Они называются пустыми полями, потому что в качестве имени поля используется идентификатор blank .

На них нельзя ссылаться (так же, как и на любую переменную, у которой в качестве имени указан пустой идентификатор).) но они принимают участие в структуре памяти структуры.Обычно и практически они используются как заполнение, чтобы выровнять последующие поля с позициями байтов (или позициями в памяти), которые соответствуют расположению данных, поступающих (или идущих) в другую систему.Выигрыш в том, что эти структурные значения (или, скорее, их пространство памяти) могут быть просто и эффективно выгружены или прочитаны за один шаг.

@ mkopriva подробно описывает, для чего предназначен конкретный вариант использования из вопроса.

Слово предупреждения: эти пустые поля как "аннотации типов" следует использовать с осторожностью, поскольку они добавляют ненужные издержки к всем (!) Значениям такой структуры.К этим полям нельзя обращаться, но они все еще требуют памяти.Если вы добавите пустое поле, размер которого составляет 8 байтов (например, int64), если вы создадите миллион элементов, эти 8 байтов будут считаться миллион раз.Таким образом, это «некорректное» использование пустых полей: намерение состоит в том, чтобы добавить метаинформацию к самому типу (не к его экземплярам), однако стоимость заключается в том, что для всех элементов потребуется увеличенная память.

Можно сказать, что вы используете тип с размером 0, например struct{}.Лучше, как если бы он использовался в правильной позиции (например, являясь первым полем, см. Структура имеет другой размер, если порядок полей отличается ; а также Почему позиция байта `[0]`в структуре имеет значение? ), они не изменят размер структуры.Тем не менее, код, который использует отражение для итерации по полям структуры, все равно должен будет циклически проходить по ним, поэтому он делает такой код менее эффективным (как правило, весь процесс маршалинга / демаршалинга).Кроме того, поскольку теперь мы не можем использовать произвольный тип, мы теряем преимущество переноса информации о типе.

Это последнее утверждение (о том, что при использовании struct{} мы теряем информацию о переносимом типе) можно обойти.struct{} не единственный тип с размером 0, все массивы с длиной 0 также имеют нулевой размер (независимо от фактического типа элемента).Таким образом, мы можем сохранить информацию о типе, используя массив размером 0, который мы хотели бы включить, например:

type CustomLabel struct {
    _ [0]func() `constructor:"init"`
    _ [0]string `property:"text"`
}

Теперь этот тип CustomLabel выглядит намного лучше с точки зрения производительности, так какрассматриваемый тип: его размер по-прежнему равен 0. И все еще можно получить доступ к типу элемента массива, используя Type.Elem(), как в этом примере:

type CustomLabel struct {
    _ [0]func() `constructor:"init"`
    _ [0]string `property:"text"`
}

func main() {
    f := reflect.ValueOf(CustomLabel{}).Type().Field(0)
    fmt.Println(f.Tag)
    fmt.Println(f.Type)
    fmt.Println(f.Type.Elem())
}

Вывод (попробуйте на Go Playground):

constructor:"init"
[0]func()
func()
0 голосов
/ 17 мая 2018

Вы можете думать о нем как о мета-информации о типе, она не доступна через экземпляр этого типа, но может быть доступна с помощью reflect или go/ast.Это дает заинтересованному пакету / программе некоторые директивы относительно того, что делать с этим типом.Например, на основе этих тегов он может генерировать код с помощью go: generate.

Учитывая, что один из тегов говорит constructor:"init", а тип поля - func(), весьма вероятно, что это используется с go: generateдля генерации функции конструктора или метода инициализатора с именем init для типа CustomLabel.


Вот пример использования reflect для получения «мета» информации (хотя, как я уже сказал,упоминалось, что конкретный пример qt, вероятно, предназначен для обработки go: generate).

type CustomLabel struct {
    _ func() `constructor:"init"`
    _ string `property:"text"`
}

fmt.Println(reflect.ValueOf(CustomLabel{}).Type().Field(0).Tag)
// constructor:"init"

fmt.Println(reflect.ValueOf(CustomLabel{}).Type().Field(0).Type)
// func()

https://play.golang.org/p/47yWG4U0uit

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...