Какова цель и тип возвращаемого значения оператора __builtin_offsetof? - PullRequest
6 голосов
/ 30 декабря 2008

Какова цель оператора __builtin_offsetof (или оператора _FOFF в Symbian) в C ++?

Кроме того, что это возвращает? Указатель? Количество байтов?

Ответы [ 4 ]

13 голосов
/ 30 декабря 2008

Как указывает @litb и как показывает @JesperE, offsetof () предоставляет целочисленное смещение в байтах (как значение size_t).

Когда вы могли бы использовать его?

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

#include <stddef.h>

typedef stuct config_info config_info;
struct config_info
{
   int parameter1;
   int parameter2;
   int parameter3;
   char *string1;
   char *string2;
   char *string3;
   int parameter4;
} main_configuration;

typedef struct config_desc config_desc;
static const struct config_desc
{
   char *name;
   enum paramtype { PT_INT, PT_STR } type;
   size_t offset;
   int   min_val;
   int   max_val;
   int   max_len;
} desc_configuration[] =
{
    { "GIZMOTRON_RATING", PT_INT, offsetof(config_info, parameter1), 0, 100, 0 },
    { "NECROSIS_FACTOR",  PT_INT, offsetof(config_info, parameter2), -20, +20, 0 },
    { "GILLYWEED_LEAVES", PT_INT, offsetof(config_info, parameter3), 1, 3, 0 },
    { "INFLATION_FACTOR", PT_INT, offsetof(config_info, parameter4), 1000, 10000, 0 },
    { "EXTRA_CONFIG",     PT_STR, offsetof(config_info, string1), 0, 0, 64 },
    { "USER_NAME",        PT_STR, offsetof(config_info, string2), 0, 0, 16 },
    { "GIZMOTRON_LABEL",  PT_STR, offsetof(config_info, string3), 0, 0, 32 },
};

Теперь вы можете написать общую функцию, которая читает строки из файла конфигурации, отбрасывая комментарии и пустые строки. Затем он изолирует имя параметра и ищет его в таблице desc_configuration (которую вы можете отсортировать, чтобы можно было выполнить бинарный поиск - с этим обращаются несколько вопросов SO). Когда он находит правильную запись config_desc, он может передать найденное значение и запись config_desc одной из двух подпрограмм - одна для обработки строк, другая для обработки целых чисел.

Ключевая часть этих функций:

static int validate_set_int_config(const config_desc *desc, char *value)
{
    int *data = (int *)((char *)&main_configuration + desc->offset);
    ...
    *data = atoi(value);
    ...
}

static int validate_set_str_config(const config_desc *desc, char *value)
{
    char **data = (char **)((char *)&main_configuration + desc->offset);
    ...
    *data = strdup(value);
    ...
}

Это позволяет избежать написания отдельной функции для каждого отдельного члена структуры.

8 голосов
/ 30 декабря 2008

Это встроенная функция, предоставляемая компилятором GCC для реализации макроса offsetof, определенного стандартом C и C ++:

GCC - смещение

Возвращает смещение в байтах, в котором находится член структуры / объединения POD.

Пример:

struct abc1 { int a, b, c; };
union abc2 { int a, b, c; };
struct abc3 { abc3() { } int a, b, c; }; // non-POD
union abc4 { abc4() { } int a, b, c; };  // non-POD

assert(offsetof(abc1, a) == 0); // always, because there's no padding before a.
assert(offsetof(abc1, b) == 4); // here, on my system
assert(offsetof(abc2, a) == offsetof(abc2, b)); // (members overlap)
assert(offsetof(abc3, c) == 8); // undefined behavior. GCC outputs warnings
assert(offsetof(abc4, a) == 0); // undefined behavior. GCC outputs warnings

@ Джонатан приводит хороший пример того, где вы можете его использовать. Я помню, что видел, как он использовался для реализации навязчивых списков (списков, чьи элементы данных включают сами указатели next и prev), но я не могу вспомнить, где это было полезно при его реализации, к сожалению.

7 голосов
/ 02 января 2009

Назначение встроенного оператора __offsetof состоит в том, что поставщик компилятора может продолжать #define макрос offsetof (), но при этом работать с классами, которые определяют унарный оператор &. Типичное определение макроса C для offsetof () работает только тогда, когда (& lvalue) возвращает адрес этого rvalue. То есть

#define offsetof(type, member) (int)(&((type *)0)->member) // C definition, not C++
struct CFoo {
    struct Evil {
        int operator&() { return 42; }
    };
    Evil foo;
};
ptrdiff_t t = offsetof(CFoo, foo); // Would call Evil::operator& and return 42
2 голосов
/ 30 декабря 2008

Как и @litb, сказал: смещение в байтах члена структуры / класса. В C ++ есть случаи, когда он не определен, если компилятор будет жаловаться. IIRC, один из способов его реализации (по крайней мере, в C) - сделать

#define offsetof(type, member) (int)(&((type *)0)->member)

Но я уверен, что есть проблемы, но я оставлю это заинтересованному читателю, чтобы он указал ...

...