Сложная compile-time инициализация структуры
От: CoolCmd Россия  
Дата: 25.09.07 20:10
Оценка:
Добрый!

Нужна инициализация избранных полей структуры в зависимости от значения Id. Делается это во время определения массива этих структур.
// в реальном коде она сложнее :)
struct S
{
    int Id;
    union
    {
        struct
        {
            char c;
        } Id1;
        struct
        {
            __int64 i64;
        } Id2;
    };
};

Т.е. при значении Id=1 нужно инициализировать переменную с, при Id=2 — i64, а при Id=0 вообще ничего не инициализировать!

Проблема
1) union дает доступ только к первой переменной/структуре
2) Если я инициализирую только Id, то остальное компилятор обнуляет, а это лишний код

Я поискал по форуму и понял, что нужно использовать конструкторы. Но объекты я пока не изучал, т.к. тема мутная, синтаксис объектов мутный, в форуме в каждом втором сообщении мутные цитаты из стандарта... Может кто-нибудь мне привести конкретный пример? Нужно с чего-то начать.

По второй проблеме наткнулся в этом форуме на мутную цитату из стандарта

— if T is a reference type, no initialization is performed.

Что есть reference type? Может запихнуть его первым полем union-а, что бы избежать ненужного обнуления?
простите, я убил небо
Re: Сложная compile-time инициализация структуры
От: Erop Россия  
Дата: 25.09.07 20:40
Оценка:
Здравствуйте, CoolCmd, Вы писали:

CC>Т.е. при значении Id=1 нужно инициализировать переменную с, при Id=2 — i64, а при Id=0 вообще ничего не инициализировать!


А чем инициализировать?

CC>Что есть reference type? Может запихнуть его первым полем union-а, что бы избежать ненужного обнуления?


Это тебе не нужно.
Ты лучше объясни что за массив? Если статический или глобальный, то время на инициализацию нулями не тратится, если временная переменная в функции (автоматический массив), т о тратится.
Но тогда можешь написать конструктор у своей структуры.
Но я не понимаю, зачем тебе её вообще инициализировать чем-то?
По умолчанию (если не будешь определять конструктор) твоя структура в автоматическом массиве вообще никак инициализироваться не будет...

КОнструктор пишут так:
struct S
{
    S()   //  Имя конструктора совпадает с именем структуры
    {
        // Этот конструктор, например, вообще ничего не делает
        //  А если раскомментарить следующую строку, то будет инициализировать Id в ноль
        //  Id = 0;
    }
    int Id;
    union
    {
        struct
        {
            char c;
        } Id1;
        struct
        {
            __int64 i64;
        } Id2;
    };
};
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Сложная compile-time инициализация структуры
От: Аноним  
Дата: 25.09.07 21:37
Оценка:
Здравствуйте, Erop, Вы писали:

E>А чем инициализировать?

Значениями, рассчитываемыми в функции. Я неудачно название темы выбрал , не compile-time, а run-time, но во время определения массива.
Т.е. сейчас это делается примерно так:
S HugeArray[] ={{1, byteLocalVariable1 + 37}, {2, &LocalVariable2}, {0}};

В итоге вместо байта сохраняется 8 байт (все валится в ULONG_PTR), плюс лишние нули для последнего элемента.

E>Ты лучше объясни что за массив? Если статический или глобальный, то время на инициализацию нулями не тратится, если временная переменная в функции (автоматический массив), т о тратится.

Локальный массив в функции. Я в дебагере смотрел, что там нагенерилось.

E>Но тогда можешь написать конструктор у своей структуры.

E>Но я не понимаю, зачем тебе её вообще инициализировать чем-то?
Эта байда передается драйверу, который ее обрабатывает. Если не определить массив, то придется вызывать драйвер для каждой структуры, а переход из r3 в r0 и обратно в NT занимает много времени.

E>По умолчанию (если не будешь определять конструктор) твоя структура в автоматическом массиве вообще никак инициализироваться не будет...

E>КОнструктор пишут так:
E>
struct S
E>{
E>    S()   //  Имя конструктора совпадает с именем структуры
E>    {
E>        // Этот конструктор, например, вообще ничего не делает
E>        //  А если раскомментарить следующую строку, то будет инициализировать Id в ноль
E>        //  Id = 0;
E>    }
E>};

Не, мне нужна примерно вот такая возможность:
S HugeArray[] ={{Id:=1, c:=byteLocalVariable1 + 37}, {Id:=2, i64:=&LocalVariable2}, {Id:=0}};

:= я сейчас от балды придумал
Re[3]: Сложная compile-time инициализация структуры
От: Кодт Россия  
Дата: 26.09.07 09:00
Оценка:
Здравствуйте, <Аноним>, Вы писали:

А>Не, мне нужна примерно вот такая возможность:

А>
А>S HugeArray[] ={{Id:=1, c:=byteLocalVariable1 + 37}, {Id:=2, i64:=&LocalVariable2}, {Id:=0}};
А>


Если массив действительно Huge, то вряд ли ты его будешь инициализировать агрегатом — тогда и сишный код тоже будет huge
К тому же по-любому программа в каждый элемент что-то пишет, тратит на это время и инструкции.
Так что можешь совершенно спокойно использовать конструкторы.

Впрочем, если структура описана в API драйвера, то навесить на неё конструктор так просто не получится.
Тут есть 3 возможности:
— сделать класс (с нетривиальным конструктором) с идентичным layout'ом, ==> layout массива объектов этого типа будет таким же, как и у массива оригинальных структур
— — наследование
— — рукоделие
#pragma pack(push,1)
struct CS : public S // если компилятор gcc, то ещё укажи атрибут выравнивания
{
    CS() { Id=0; }
    CS(char x) { Id=1; c=x; }
    CS(int64 x) { Id=2; i64=x; }
};
#pragma pack(pop)

.....
void foo()
{
    CS arr[] = { CS('x'), CS(1LL<<48), CS() };
}

— написать make-функции, конструирующие оригинальные структуры
// можно перегрузку, а можно разноимённые функции
S makeSv()          { S s; s.Id=0;          return s; }
S makeSc(char    x) { S s; s.Id=1; s.c=x;   return s; }
S makeSi(__int64 x) { S s; s.Id=2; s.i64=x; return s; }

.....
void foo()
{
    S arr[] = { makeSc('x'), makeSi(1LL<<48), makeSv(); }
    .....
}

— ну и, наконец, банально вручную заполнять
void foo()
{
    S arr[3] = {};
    initS(arr[0], 'x'); // это макрос или функция void initS(S& dst, .....)
    initS(arr[1], 1LL<<48);
    initS(arr[2]);
}
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[4]: Сложная compile-time инициализация структуры
От: CoolCmd Россия  
Дата: 26.09.07 10:04
Оценка:
Здравствуйте, Кодт, Вы писали:

А>>Не, мне нужна примерно вот такая возможность:

А>>
А>>S HugeArray[] ={{Id:=1, c:=byteLocalVariable1 + 37}, {Id:=2, i64:=&LocalVariable2}, {Id:=0}};
А>>

К>Если массив действительно Huge, то вряд ли ты его будешь инициализировать агрегатом — тогда и сишный код тоже будет huge
Что значит "инициализировать агрегатом"? Сейчас массив заполняется с помощью вложенных макросов.

К>К тому же по-любому программа в каждый элемент что-то пишет, тратит на это время и инструкции.

Ну разумеется, чудес не бывает.

К>Так что можешь совершенно спокойно использовать конструкторы.

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

К>Впрочем, если структура описана в API драйвера, то навесить на неё конструктор так просто не получится.

Драйвер мой, могу делать все что захочу.

К>- — наследование

Была у меня мысль наплодить наследничков, но как их в массив запихнуть я точно не знаю. И как быть с их размерами, ведь они получатся разными....

К>
К>#pragma pack(push,1)
К>struct CS : public S // если компилятор gcc, то ещё укажи атрибут выравнивания
К>{
К>    CS() { Id=0; }
К>    CS(char x) { Id=1; c=x; }
К>    CS(int64 x) { Id=2; i64=x; }
К>};
К>#pragma pack(pop)

Интересный вариант, если дополнить макросами. А с push(1) проблем на x64 системах не будет?

К>- написать make-функции, конструирующие оригинальные структуры

К>
К>// можно перегрузку, а можно разноимённые функции
К>S makeSv()          { S s; s.Id=0;          return s; }
К>S makeSc(char    x) { S s; s.Id=1; s.c=x;   return s; }
К>S makeSi(__int64 x) { S s; s.Id=2; s.i64=x; return s; }
К>.....
К>void foo()
К>{
К>    S arr[] = { makeSc('x'), makeSi(1LL<<48), makeSv(); }
К>    .....
К>}
К>

Выглядит просто шикарно! Но каким образом передается структура в return s? Где она создается? Если в куче, то вариант отпадает.

К>
К>void foo()
К>{
К>    S arr[3] = {};
К>    initS(arr[0], 'x'); // это макрос или функция void initS(S& dst, .....)
К>    initS(arr[1], 1LL<<48);
К>    initS(arr[2]);
К>}
К>

Количество элементов массива подсчитать сложно, а иначе я бы наплодил функций и пробмлем не было.


Спасибо за варианты, приду домой, буду шаманить. Кстати я здесь на форуме видел еще один вариант конструктора, что-то вроде этого (по памяти пишу)
class B: _x(x) _y(y) {}.
простите, я убил небо
Re[4]: Сложная compile-time инициализация структуры
От: CoolCmd Россия  
Дата: 26.09.07 10:23
Оценка:
И еще один вопрос: можно конструктор сделать inline?
простите, я убил небо
Re: Сложная compile-time инициализация структуры
От: Sm0ke Россия ksi
Дата: 26.09.07 10:27
Оценка:
Здравствуйте, CoolCmd, Вы писали:

CC>Добрый!


CC>Нужна инициализация избранных полей структуры в зависимости от значения Id. Делается это во время определения массива этих структур.

CC>
CC>// в реальном коде она сложнее :)
CC>struct S
CC>{
CC>    int Id;
CC>    union
CC>    {
CC>        struct
CC>        {
CC>            char c;
CC>        } Id1;
CC>        struct
CC>        {
CC>            __int64 i64;
CC>        } Id2;
CC>    };
CC>};
CC>

CC>Т.е. при значении Id=1 нужно инициализировать переменную с, при Id=2 — i64, а при Id=0 вообще ничего не инициализировать!

А что если Id не в диапазоне от 0 до 2 ??

struct S
{
    int Id;
    union
    {
        struct
        {
            char c;
        } Id1;
        struct
        {
            __int64 i64;
        } Id2;
    };

  // коструктор
  S(int new_id) : Id(new_id)
  {
    switch (id)
    {
      case 1: Id1.c= 0; break;
      case 2: Id2.i64= 0; break;
    }
  }
};

int main()
{
  S s= 1, o= 2;
}
Re[5]: Сложная compile-time инициализация структуры
От: Кодт Россия  
Дата: 26.09.07 10:31
Оценка: 3 (1)
Здравствуйте, CoolCmd, Вы писали:

К>>Если массив действительно Huge, то вряд ли ты его будешь инициализировать агрегатом — тогда и сишный код тоже будет huge

CC>Что значит "инициализировать агрегатом"? Сейчас массив заполняется с помощью вложенных макросов.

Это форма записи с фигурными скобками.

К>>Впрочем, если структура описана в API драйвера, то навесить на неё конструктор так просто не получится.

CC>Драйвер мой, могу делать все что захочу.

Ну если тебе не жалко заточить API драйвера под C++ — то пожалуйста.

К>>- — наследование

CC>Была у меня мысль наплодить наследничков, но как их в массив запихнуть я точно не знаю. И как быть с их размерами, ведь они получатся разными....

Можно сформировать массив указателей на объекты — а сами объекты разместить где-то рядом. (Естественно, драйвер должен быть готов к такому).

Можно сериализовать объекты (в массив байтов, или в структуру, поля которой — разнотипные объекты, или ещё каким способом).

CC>Интересный вариант, если дополнить макросами. А с push(1) проблем на x64 системах не будет?

А какие проблемы? Базовый подобъект сохраняет свой layout, это просто гарантия того, что в наследник не будут напиханы хвостовые пробелы по воле компилятора.
Скажем, если sizeof(S)=3, то без выравнивания может получиться sizeof(CS)=4. А с выравниванием — sizeof(CS)==sizeof(S).

К>>- написать make-функции, конструирующие оригинальные структуры

К>>
К>>// можно перегрузку, а можно разноимённые функции
К>>S makeSv()          { S s; s.Id=0;          return s; }
К>>S makeSc(char    x) { S s; s.Id=1; s.c=x;   return s; }
К>>S makeSi(__int64 x) { S s; s.Id=2; s.i64=x; return s; }
К>>.....
К>>void foo()
К>>{
К>>    S arr[] = { makeSc('x'), makeSi(1LL<<48), makeSv(); }
К>>    .....
К>>}
К>>

CC>Выглядит просто шикарно! Но каким образом передается структура в return s? Где она создается? Если в куче, то вариант отпадает.

Учите матчасть, дорогой Ватсон! Всё создаётся исключительно на стеке. (Между прочим, это легальный сишный код, а не исключительно С++ный).
Более того, если компилятор догадается сделать оптимизацию NRVO, то адреса внутренних переменных s совпадут с адресами элементов массива.
В случае, если функции inline, таких шансов больше, но и если они extern, то тоже вполне может получиться.

Так что скрещивай сию методу со своими макросами, и будет тебе щасте.

CC>Спасибо за варианты, приду домой, буду шаманить. Кстати я здесь на форуме видел еще один вариант конструктора, что-то вроде этого (по памяти пишу)

CC>
CC>class B: _x(x) _y(y) {}.
CC>

Это не С++ Что-то напутал по памяти.
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[5]: Сложная compile-time инициализация структуры
От: Кодт Россия  
Дата: 26.09.07 10:40
Оценка:
Здравствуйте, CoolCmd, Вы писали:

CC>И еще один вопрос: можно конструктор сделать inline?


Точно так же, как и обычную функцию.
class X
{
public:
    X();
    X(int);
    X(char) { } // inline
    inline X(double) { } // inline, можно было ключевое слово не писать
};

inline X::X() { }

// в недрах .cpp

X::X(int) { } // не inline

Причём если ты не объявил конструктор копирования, оператор присваивания, деструктор, а то и дефолтный конструктор — все они будут созданы компилятором и проинлайнены.
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[2]: Сложная compile-time инициализация структуры
От: Кодт Россия  
Дата: 26.09.07 10:40
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>А что если Id не в диапазоне от 0 до 2 ??


Id — это тэг вариантного типа. Не нужно его явно передавать.

А уж делать конструкторы не explicit — в данном контексте выглядит злодейством.
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[6]: Сложная compile-time инициализация структуры
От: CoolCmd Россия  
Дата: 26.09.07 22:29
Оценка:
Здравствуйте, Кодт, Вы писали:

К>>>- — наследование

CC>>Была у меня мысль наплодить наследничков, но как их в массив запихнуть я точно не знаю. И как быть с их размерами, ведь они получатся разными....
К>Можно сформировать массив указателей на объекты — а сами объекты разместить где-то рядом. (Естественно, драйвер должен быть готов к такому).
Пока я не представляю, как разместить "где-то рядом".

К>>>- написать make-функции, конструирующие оригинальные структуры

CC>>Выглядит просто шикарно! Но каким образом передается структура в return s? Где она создается? Если в куче, то вариант отпадает.
К>Учите матчасть, дорогой Ватсон! Всё создаётся исключительно на стеке. (Между прочим, это легальный сишный код, а не исключительно С++ный).
К>Более того, если компилятор догадается сделать оптимизацию NRVO, то адреса внутренних переменных s совпадут с адресами элементов массива.
К>В случае, если функции inline, таких шансов больше, но и если они extern, то тоже вполне может получиться.
Мне даже в голову не приходило так делать, ведь это теоретически не эффективно. Сейчас проверил на примере, так и оказалось.
enum OPERATION_ID {OP_ID_0, OP_ID_1, OP_ID_2};

struct S
{
public:
    OPERATION_ID Id;
    union
    {
        struct
        {
            int *Param1;
            int Param2;
        } Id1Parameters;
        struct
        {
            char Param1;
        } Id2Parameters;
    };
};

inline S Operation0()
{
    S s;
    s.Id = OP_ID_0;
    return s;
}

inline S Operation1(int *p1, int p2)
{
    S s;
    s.Id = OP_ID_1;
    s.Id1Parameters.Param1 = p1;
    s.Id1Parameters.Param2 = p2;
    return s;
}

inline S Operation2(char p1)
{
    S s;
    s.Id = OP_ID_2;
    s.Id2Parameters.Param1 = p1;
    return s;
}

void TestFunc()
{
    int n;
    S arr[] =
    {
        Operation0(),
        Operation1(&n, 10),
        Operation2('a')
    };
}

Вот что выдал компилятор M$ VS2005:
        Operation0(),
004030AA  and         dword ptr [ebp-10h],0 
004030AE  push        esi  
004030AF  push        edi  
004030B0  lea         esi,[ebp-10h] 
004030B3  lea         edi,[arr] 
004030B6  movs        dword ptr es:[edi],dword ptr [esi] 
004030B7  movs        dword ptr es:[edi],dword ptr [esi] 
004030B8  movs        dword ptr es:[edi],dword ptr [esi] 
        Operation1(&n, 10),
004030B9  xor         eax,eax 
004030BB  inc         eax  
004030BC  mov         dword ptr [ebp-10h],eax 
004030BF  mov         dword ptr [ebp-8],0Ah 
004030C6  lea         ecx,[n] 
004030C9  mov         dword ptr [ebp-0Ch],ecx 
004030CC  lea         esi,[ebp-10h] 
004030CF  lea         edi,[ebp-28h] 
004030D2  movs        dword ptr es:[edi],dword ptr [esi] 
004030D3  movs        dword ptr es:[edi],dword ptr [esi] 
004030D4  movs        dword ptr es:[edi],dword ptr [esi] 
        Operation2('a')
004030D5  mov         dword ptr [ebp-10h],2 
004030DC  mov         byte ptr [ebp-0Ch],61h 
004030E0  lea         esi,[ebp-10h] 
004030E3  lea         edi,[ebp-1Ch] 
004030E6  movs        dword ptr es:[edi],dword ptr [esi] 
004030E7  movs        dword ptr es:[edi],dword ptr [esi] 
004030E8  movs        dword ptr es:[edi],dword ptr [esi]

Грустное, удручающее зрелище.

К>Так что скрещивай сию методу со своими макросами, и будет тебе щасте.

Не, нам такой хоккей не нужен.

После этого я попробовал вариант с нормальным конструктором. Вот что получилось:
enum OPERATION_ID {OP_ID_0, OP_ID_1, OP_ID_2};

struct S
{
public:
    OPERATION_ID Id;
    union
    {
        struct
        {
            int *Param1;
            int Param2;
        } Id1Parameters;
        struct
        {
            char Param1;
        } Id2Parameters;
    };
    S() {
        Id = OP_ID_0;
    };
    S(int *p1, int p2) {
        Id = OP_ID_1;
        Id1Parameters.Param1 = p1;
        Id1Parameters.Param2 = p2;
    };
    S(char p1) {
        Id = OP_ID_2;
        Id2Parameters.Param1 = p1;
    };
};

void TestFunc()
{
    int n;
    S arr[] =
    {
        S(),
        S(&n, 10),
        S('a')
    };
}

        S(),
004030AA  and         dword ptr [arr],0 
        S(&n, 10),
004030AE  xor         eax,eax 
004030B0  inc         eax  
004030B2  mov         dword ptr [ebp-1Ch],eax 
004030B8  lea         ecx,[n] 
004030BC  mov         dword ptr [ebp-18h],ecx 
004030BF  mov         dword ptr [ebp-14h],0Ah 
        S('a')
004030C6  mov         dword ptr [ebp-10h],2 
004030CD  mov         byte ptr [ebp-0Ch],61h

Совсем другое дело, ничего лишнего!
Но этот вариант неудобный, т.к. нужный конструктор выбирается на основе передаваемых параметром, а это легко может привести к путанице (не в этом примере). Так что придется писать макросы с приведением параметром, но и в этом варианте нет уверенности...

Сдесь мне пришел в голову другой вариант: в базовом классе оставить данные, и наплодить наследников с нужными конструкторами.
enum OPERATION_ID {OP_ID_0, OP_ID_1, OP_ID_2};

struct S
{
public:
    OPERATION_ID Id;
    union
    {
        struct
        {
            int *Param1;
            int Param2;
        } Id1Parameters;
        struct
        {
            char Param1;
        } Id2Parameters;
    };
};

struct Operation0 : public S
{
public:
    Operation0() {
        Id = OP_ID_0;
    };
};

struct Operation1 : public S
{
public:
    Operation1(int *p1, int p2) {
        Id = OP_ID_1;
        Id1Parameters.Param1 = p1;
        Id1Parameters.Param2 = p2;
    };
};

struct Operation2 : public S
{
public:
    Operation2(char p1)
    {
        Id = OP_ID_2;
        Id2Parameters.Param1 = p1;
    };
};

void TestFunc()
{
    int n;
    S arr[] =
    {
        Operation0(),
        Operation1(&n, 10),
        Operation2('a')
    };
}

Результат оказался в точности как в первом примере, т.е. с лишними инструкциями копирования.
Почему в данном случае нет оптимизации? Компилятор тупо не делает сравнение на размер объектов?
Можно это как-то исправить? Может я наследование как-то неверно сделал.


И еще непонятка. Вот на этот вариант:
struct Operation2 : public S
{
public:
    Operation2(char p1) : Id(OP_ID_2) {
        //Id = OP_ID_2;
        Id2Parameters.Param1 = p1;
    };
};

компилятор ругается

error C2614: 'Operation2' : illegal member initialization: 'Id' is not a base or member

простите, я убил небо
Re[7]: Сложная compile-time инициализация структуры
От: Erop Россия  
Дата: 27.09.07 07:07
Оценка: 13 (2)
Здравствуйте, CoolCmd, Вы писали:

CC>И еще непонятка. Вот на этот вариант:

CC>
CC>    Operation2(char p1) : Id(OP_ID_2) {
CC>



CC>компилятор ругается

CC>
error C2614: 'Operation2' : illegal member initialization: 'Id' is not a base or member

Ты можешь инициализировать только свои базы и поля!!! Поля базы должна инициализировать база!!!

Есть, кстати, ещё и такой путь:
struct S {
    // тут данные

    S& SetOp0() // возвращает ссылку на следующий объект в векторе
    {
        // тут инициализируешь как надо и 
        return this[1];
    }
    S& SetOp1( int ) // возвращает ссылку на следующий объект в векторе
    {
        // тут инициализируешь как надо и 
        return this[1];
    }
    S& SetOp2( char ) // возвращает ссылку на следующий объект в векторе
    {
        // тут инициализируешь как надо и 
        return this[1];
    }
};



Ну а инициализацию пишешь как-то так:
S vec[4];
S& endOfInit = vec[0].SetOp1( 5 ).SetOp2( 'a' ).SetOp0().SetOp1( 8 );
assert( &endOfInit == vec + lengthof( vec ) );




Ещё есть путь через продвинутые шаблоны. Типа создаёшь объект-инициализатор, в котором в самом типе хранится какой элемент вектора чем инициализировать, а в объекте параметры, а потом натравливаеь его на инициализируемый вектор. Пример использования может быть каким-то таким:
S vec[4];
SInitializer( vec ).Op1( 5 ).Op2( 'E' ).Op0().Op2( 18 ).Init();


Правда мне лично больше всего нравится таки инициализация без извращений, в таком вот виде:
S vec[4];
vec[0].SetOp1( 5 );
vec[1].SetOp2( '!' );
vec[2].SetOp0();
vec[3].SetOp1( 18 );


Ну или, как крайний вариант, такая штука:
struct S {
    // тут данные
    
    // Операция 0
    enum Op0_t { Op0 };
    S( Op0_t ) { InitOp0(); }

    // Операция 1
    enum Op1_t { Op1 };
    S( Op1_t, int i ) { InitOp1( i ); }

    // Операция 2
    enum Op2_t { Op2 };
    S( Op2_t, char ch ) { InitOp2( ch ); }
    
};

// Ну и теперь пользуемся понаписанными чудесами:
S vec[] = { S( S::Op1, 5 ), S( S::Op2, 'A' + 4 ), S( S::Op0 ), S( S::Op1, 18 ) };
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[7]: Сложная compile-time инициализация структуры
От: Кодт Россия  
Дата: 27.09.07 09:08
Оценка:
Здравствуйте, CoolCmd, Вы писали:

К>>Более того, если компилятор догадается сделать оптимизацию NRVO, то адреса внутренних переменных s совпадут с адресами элементов массива.

К>>В случае, если функции inline, таких шансов больше, но и если они extern, то тоже вполне может получиться.
CC>Мне даже в голову не приходило так делать, ведь это теоретически не эффективно. Сейчас проверил на примере, так и оказалось.

В релизе? Ну, значит, не судьба.

CC>После этого я попробовал вариант с нормальным конструктором. Вот что получилось:

CC>Совсем другое дело, ничего лишнего!
CC>Но этот вариант неудобный, т.к. нужный конструктор выбирается на основе передаваемых параметром, а это легко может привести к путанице (не в этом примере). Так что придется писать макросы с приведением параметром, но и в этом варианте нет уверенности...

Это делается элементарно: если ты не уверен в том, что сигнатуры конструкторов выберутся правильно на основе "существенных" параметров, — добавь несущественные:
template<OPERATION_ID Id> struct id2type // самое главное - вот это
{
    operator OPERATION_ID () const { return Id; } // а это так, сахарку насыпал
};

#define TAG(Id)  (id2type<Id>())

struct S
{
    .....
    explicit S( id2type<OP_ID_0> tag                  ) : Id(tag) { }
    explicit S( id2type<OP_ID_1> tag, int* p1, int p2 ) : Id(tag) { Id1Parameters.Param1 = p1; Id1Parameters.Param2 = p2; }
    explicit S( id2type<OP_ID_2> tag, char p1         ) : Id(tag) { Id2Parameters.Param1 = p1; }
};

#define S_0()      S(TAG(OP_ID_0))
#define S_1(p1,p2) S(TAG(OP_ID_1),p1,p2)
#define S_2(p1)    S(TAG(OP_ID_2),p1)

Если окажется, что передача тэга по значению стоит слишком дорого, попробуй заменить его на указатель
template<OPERATION_ID Id> struct id2type
{
};

#define TAG(Id)  ((id2type<OP_ID_0>*)NULL)

// извлекатель идентификатора
template<OPERATION_ID Id> type2id(id2type<Id>* _) { return Id; }

struct S
{
    .....
    explicit S( id2type<OP_ID_0>* tag ) : Id(type2id(tag)) { }
    .....
};


CC>Сдесь мне пришел в голову другой вариант: в базовом классе оставить данные, и наплодить наследников с нужными конструкторами.

CC>Результат оказался в точности как в первом примере, т.е. с лишними инструкциями копирования.
CC>Почему в данном случае нет оптимизации? Компилятор тупо не делает сравнение на размер объектов?
CC>Можно это как-то исправить? Может я наследование как-то неверно сделал.

Потому что конструкторы наследников не шибко отличаются от make-функций. Да ещё и — из-за наследования — никакие оптимизации (кроме инлайна, естественно) оказываются невозможными.
Если в первом случае компилятор просто не додумался до NRVO, то в этом ему просто запрещено его делать.
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[8]: Сложная compile-time инициализация структуры
От: CoolCmd Россия  
Дата: 27.09.07 18:56
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Это делается элементарно: если ты не уверен в том, что сигнатуры конструкторов выберутся правильно на основе "существенных" параметров, — добавь несущественные:

Ого, я так пока и не понял, как работает этот "элементарный" код. До операторов и шаблонов пока не добрался.

К>Если окажется, что передача тэга по значению стоит слишком дорого, попробуй заменить его на указатель

Да нет, все замечательно , доп. машинного кода вообще не добавилось, как и в варианте Erop-а.
Вот думаю, какой оставить. Наверное Erop-а, т.к. он более понятный.

CC>>Результат оказался в точности как в первом примере, т.е. с лишними инструкциями копирования.

CC>>Почему в данном случае нет оптимизации? Компилятор тупо не делает сравнение на размер объектов?
CC>>Можно это как-то исправить? Может я наследование как-то неверно сделал.
К>Потому что конструкторы наследников не шибко отличаются от make-функций. Да ещё и — из-за наследования — никакие оптимизации (кроме инлайна, естественно) оказываются невозможными.
К>Если в первом случае компилятор просто не додумался до NRVO, то в этом ему просто запрещено его делать.
А почему запрещено? Если нет ничего виртуального и размеры объектов равны, то ИМХО можно смело оптимизировать, ведь порядок данных в объекте при наследовании не меняется.
простите, я убил небо
Re[8]: Сложная compile-time инициализация структуры
От: CoolCmd Россия  
Дата: 27.09.07 19:01
Оценка:
Здравствуйте, Erop, Вы писали:

CC>>И еще непонятка. Вот на этот вариант:

CC>>
CC>>    Operation2(char p1) : Id(OP_ID_2) {
CC>>

E>Ты можешь инициализировать только свои базы и поля!!! Поля базы должна инициализировать база!!!
Ясно. А зачем вообще нужны инициализации вида : Id(OP_ID_2) { если тоже самое можно сделать обычным присваиванием в конструкторе без всякого гемороя? У меня в справке и книге об этом ничего нет. Кстати, вариант : Id1Parameters.Param1(p1) { тоже не жрет — на точку ругается.

E> S& SetOp2( char ) // возвращает ссылку на следующий объект в векторе

E> {
E> // тут инициализируешь как надо и
E> return this[1];
E> }
E>S& endOfInit = vec[0].SetOp1( 5 ).SetOp2( 'a' ).SetOp0().SetOp1( 8 );
E>assert( &endOfInit == vec + lengthof( vec ) );[/c]
Вроде понятно, только this[1] хакерскими приемчиками попахивает, нет?

E>Правда мне лично больше всего нравится таки инициализация без извращений, в таком вот виде:
S vec[4];
E>vec[0].SetOp1( 5 );
E>vec[1].SetOp2( '!' );
E>vec[2].SetOp0();
E>vec[3].SetOp1( 18 );

Мне тоже, но неудобно, да и конструкторы в этом случае не нужны — обычной структуры хватит.

E>Ну или, как крайний вариант, такая штука:

Не понял, почему крайний? Простой, понятный и рабочий вариант. Наверное его и оставлю. Большое спасибо.

E>
struct S {
E>    // тут данные
E>    // Операция 0
E>    enum Op0_t { Op0 };
E>    S( Op0_t ) { InitOp0(); }
E>// Ну и теперь пользуемся понаписанными чудесами:
E>S vec[] = { S( S::Op1, 5 ), S( S::Op2, 'A' + 4 ), S( S::Op0 ), S( S::Op1, 18 ) };
E>
простите, я убил небо
Re[9]: Сложная compile-time инициализация структуры
От: Erop Россия  
Дата: 27.09.07 22:42
Оценка:
Здравствуйте, CoolCmd, Вы писали:

E>>Ты можешь инициализировать только свои базы и поля!!! Поля базы должна инициализировать база!!!

CC>Ясно. А зачем вообще нужны инициализации вида : Id(OP_ID_2) { если тоже самое можно сделать обычным присваиванием в конструкторе без всякого гемороя? У меня в справке и книге об этом ничего нет.
Некоторые поля и базы нельзя инициализировать по умолчанию...
Например ссылку или открытый файл или ещё чего...

CC>Кстати, вариант : Id1Parameters.Param1(p1) { тоже не жрет — на точку ругается.

1) Я так понял, что имя класса, а не объекта. ТОгда надо писать ::
2) Так тоже нельзя, можно так:
Id1ParametersOp1() : Id1Parameters( p1 ) {};

Где Id1Parameters( p1 ) -- это вызов конструктора базы, который, в свою очередь устроен так:
Id1Parameters( int p ) : Param1( p ) {};


CC>Вроде понятно, только this[1] хакерскими приемчиками попахивает, нет?

Пока ты инициализируешь элементы вектора всё абсолютно законно...

E>>Правда мне лично больше всего нравится таки инициализация без извращений, в таком вот виде:
S vec[4];
E>>vec[0].SetOp1( 5 );
E>>vec[1].SetOp2( '!' );
E>>vec[2].SetOp0();
E>>vec[3].SetOp1( 18 );

CC>Мне тоже, но неудобно, да и конструкторы в этом случае не нужны — обычной структуры хватит.
Да, конструкторы не нужны, код оптиальный, структура POD и без всяких извратов. Да и понятно всё, кстати...

E>>Ну или, как крайний вариант, такая штука:

CC>Не понял, почему крайний? Простой, понятный и рабочий вариант. Наверное его и оставлю. Большое спасибо.

Хозяин -- барин
Обращайся
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[9]: Сложная compile-time инициализация структуры
От: Erop Россия  
Дата: 27.09.07 22:44
Оценка:
Здравствуйте, CoolCmd, Вы писали:

К>>Если в первом случае компилятор просто не додумался до NRVO, то в этом ему просто запрещено его делать.

CC>А почему запрещено? Если нет ничего виртуального и размеры объектов равны, то ИМХО можно смело оптимизировать, ведь порядок данных в объекте при наследовании не меняется.

Ну вообще-то надо бы ещё и копировать. Но ненужное копирование компилятор мог бы и выбрасывать, конечно, но он мог бы и в варианте с функциями-мэйкерами
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[9]: Сложная compile-time инициализация структуры
От: Кодт Россия  
Дата: 28.09.07 10:24
Оценка:
Здравствуйте, CoolCmd, Вы писали:

К>>Это делается элементарно: если ты не уверен в том, что сигнатуры конструкторов выберутся правильно на основе "существенных" параметров, — добавь несущественные:

CC>Ого, я так пока и не понял, как работает этот "элементарный" код. До операторов и шаблонов пока не добрался.

Да всё просто: теперь все конструкторы различаются не только по количеству аргументов, но и по типам. Причём так, что невозможно сделать случайное приведение.
struct S
{
    explicit S( char c ) : Id(OP_ID_Char) { ..... }
    explicit S( int  i ) : Id(OP_ID_Int) { ..... }
};

void foo()
{
    S arr[] =
    {
        S( 'x' ), // char
        S( 'x'+1 ), // int
    };
}


Чтобы не лепить типы-тэги вручную, и был сделан шаблон id2type. Плюс этого шаблона в том, что из него можно извлекать значение тэга — это уменьшает риск очепяток
struct S
{
    explicit S( id2type<OP_ID_0> tag ) : Id(OP_ID_1) {}
    .....
}
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.