Невероятно, но факт! Не константные значения в компайл-тайм!
От: remark Россия http://www.1024cores.net/
Дата: 06.02.07 20:55
Оценка: 257 (23) -2 :)
Пришло время очередного ресёрча

В компайл-тайм все объекты константные. Будь то интегральные значения или типы. Т.е. имея запись:
const int i = some::val;
typedef some::type type;

всегда some::val будет иметь одно и то же значение и some::type будет одним и тем же типом. В принципе можно к этому добавить входные агрументы:
const int i = some<char, 5>::val;
typedef some<int, 15>::type type;

но результат всё равно всегда однозначно определяется входными значениями. Т.е. это фактически функциональный язык с константными объектами.

Так вот, всё это неправда!
Сейчас вы увидите, как на стандартном с++ можно иметь переменные значения в компайл-тайм!

char engine(...);

template<typename, int> struct magic;
typedef magic<char, -1> magicc;

template<typename type = int, int id = sizeof(engine(*(magicc*)0, *(type*)0))>
struct magic
{
    friend int engine(magicc&, type&);
    static const int val = id;
};

int main()
{
    char a[magic<>::val != magic<>::val ? 1 : -1];
}






Код копилируется gcc4.1/msvc7.1/msvc8.0/edg c++

Вы скажете "Ну и что? Что с помощью этого можно сделать?"
Всё, что можно сделать, я пока не знаю, но знаю, что сделать можно много. Собственно для этого, в частности, я это и публикую, что бы общественность могла придумывать свои применения.

Вот какие применения я на данный момент придумал:

1. Компайл-тайм счётчик. Аналог __COUNTER__, тока лучше. Фичи:
— в любом месте можно завести свой "личный" экземпляр счётчика, т.е. он будет всегда начинать считать с нуля, а не с произвольного меняющегося числа.
— можно генерировать не только последовательность натуральных чисел, а произвольные последовательности: степени 2 (1, 2, 4, 8 ...), чётные числа (0, 2, 4, 6 ...), в сторону уменьшения (0, -1, -2, -3 ...) и т.д.
— если счётчик используется внутри шаблона, будет генерироваться новое значение для каждой специализации шаблона
— можно получить текущее значение счётчика, не инкрементируя его
— типизированный счётчик — выдаёт, например, значения типа short

Примеры использования:

// Просто получаем значение счётчика
int i = cnt<>::val;

// Заводим "личный" счётчик
struct my_tag;
int j = cnt<my_tag>::val;

// Генерируем последовательность степеней двойки
struct my_tag2;
enum x
{
  val1 = cnt<my_tag2, pow2gen>::val,
  val2 = cnt<my_tag2, pow2gen>::val,
  val3 = cnt<my_tag2, pow2gen>::val
};



2. Компайл-тайм генератор случайных чисел.
Никаких особых фич и распределений я не делал. Пример использования:

// Создали массив из 3 случайных чисел от 5 до 20 
int data[] = {rnd<5, 20>::val, rnd<5, 20>::val, rnd<5, 20>::val};



3. ... не знаю как назвать... легче на примере показать. Допустим есть класс:

struct person
{
    prop<person, int> m1;
    prop<person, char> m2;
    prop<person, std::string> m3;
    prop<person, char> m4;
    prop<person, int> m5;
};


Каждый член класса в компайл-тайм получает значение смещения себя относительно объемлющего объекта. Т.е. фактически получается что-то типа следующего:

struct person
{
    prop<person, int, 0> m1;
    prop<person, char, 5> m2;
    prop<person, std::string, 8> m3;
    prop<person, char, 40> m4;
    prop<person, int, 44> m5;
};


Как это работает? Заводится приватный счётчик (cnt<person>). Каждый член получает текущее значение этого счётчика — это есть его смещение. Далее инкрементирует счётчик на свой размер. И уаля. В реальности ещё приходится делать поправку на выравнивание, но это уже детали.
Это даёт возможность членам обращаться к своему контейнеру и наоборот.
Добавляем к этому регистрацию типов (здесь
Автор:
Дата: 26.12.06
или здесь
Автор: Chez
Дата: 28.03.05
) и получаем не больше и не меньше, а compile-time reflection!!!
Для такого класса мы знаем порядок, типы и смещения членов — реализация, например, бинарной сериализации далее видится тривиальной


4. Это переводит
[Trick] Автоматическое определение неиспользуемых заголовков
Автор: remark
Дата: 21.01.07

[Trick] Форсирование правильного использования библиотеки
Автор: remark
Дата: 21.01.07

[Trick] Форсирование проверки возвращаемого значения
Автор: remark
Дата: 24.12.06

из разряда M$ SPECIFIC в 100% PURE C++



Дальше у кого на сколько хватит фантазии
Код указанных фич я приведу в отдельных постах, что бы сюда слишком много не мешать.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: Невероятно, но факт! Не константные значения в компайл-т
От: Андрей Тарасевич Беларусь  
Дата: 06.02.07 21:39
Оценка: 3 (1) +1
Здравствуйте, remark, Вы писали:

R>Сейчас вы увидите, как на стандартном с++ можно иметь переменные значения в компайл-тайм!


R>
R>char engine(...);

R>template<typename, int> struct magic;
R>typedef magic<char, -1> magicc;

R>template<typename type = int, int id = sizeof(engine(*(magicc*)0, *(type*)0))>
R>struct magic
R>{
R>    friend int engine(magicc&, type&);
R>    static const int val = id;
R>};

R>int main()
R>{
R>    char a[magic<>::val != magic<>::val ? 1 : -1];
R>}
R>


Хм... Оригинально, но не понятно, должно ли это работать так, как задумано. Как известно, friend declaration не вводит в охватывающий namespace видимого имени. Поэтому не ясно, почему в твоем случае при второй инстанциации 'magic' lookup для дефолтного аргумента вдруг должно найти именно "друга", а не явно объявленную оригинальную версию 'engine'...

На Comeau Online — не работает. А именно, оба 'magic<>::val' одинаковы.
Best regards,
Андрей Тарасевич
Re: Невероятно, но факт! Не константные значения в компайл-т
От: Roman Odaisky Украина  
Дата: 06.02.07 21:43
Оценка:
Здравствуйте, remark, Вы писали:

R>Пришло время очередного ресёрча


Я попробовал на VC9, только первые 2 значения разные
До последнего не верил в пирамиду Лебедева.
Re: Невероятно, но факт! Не константные значения в компайл-т
От: IROV..  
Дата: 06.02.07 21:57
Оценка:
typedef struct {} _false;
typedef struct {} _true;

char engine( ... );

template<int id = sizeof( engine( _true() ) )>
struct magic
{
    friend int engine(_true);
    static const int val = id;
};

int main()
{
    char a[magic<>::val != magic<>::val ? 1 : -1];
}


Будь проще и люди подтянутся

А то что только первое и второе вхождение будет, видно из кода, точнее из за механизма!

Вся ботва в двух ключевых моментах а именно

char engine( ... ); // выбор данной функции при перегрузке всегда последния (скока можно красивых вещей на этом сделать)

и то что sizeof() не ищет реализацию а только оглавление.

Комеаут скорее всего не работает так как надо, потомучто он не успел найти обявление friend int engine(_true);
я не волшебник, я только учусь!
Re[2]: Невероятно, но факт! Не константные значения в компай
От: remark Россия http://www.1024cores.net/
Дата: 06.02.07 22:27
Оценка:
Здравствуйте, Андрей Тарасевич, Вы писали:

АТ>Хм... Оригинально, но не понятно, должно ли это работать так, как задумано. Как известно, friend declaration не вводит в охватывающий namespace видимого имени. Поэтому не ясно, почему в твоем случае при второй инстанциации 'magic' lookup для дефолтного аргумента вдруг должно найти именно "друга", а не явно объявленную оригинальную версию 'engine'...


Да, friend declaration не вводит в охватывающий namespace видимого имени. Всё правильно. Над этой проблемой пришлось побороться.
На помощь пришёл ADL! Смотри:

class complex
{
  ...
  friend complex operator + (complex, complex);
};

complex c1, c2;
c1 + c2;


Это же работает, хотя тоже вроде как не должно, т.к. operator + () вообще говоря не виден вне класса.

Поэтому я добавил в функцию engine() параметр magic&! Теперь она тоже попадает в рассмотрение!

АТ>На Comeau Online — не работает. А именно, оба 'magic<>::val' одинаковы.


Да, я знаю... это не ко мне, это к ним


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[2]: Невероятно, но факт! Не константные значения в компай
От: remark Россия http://www.1024cores.net/
Дата: 06.02.07 22:30
Оценка:
Здравствуйте, Roman Odaisky, Вы писали:

RO>Здравствуйте, remark, Вы писали:


R>>Пришло время очередного ресёрча


RO>Я попробовал на VC9, только первые 2 значения разные


Во-первых, это уже круто!
Во-вторых, что такое VC9
В-третих, Да, всё правильно, тут я только для двух сделал, что бы было хоть чуть-чуть понятно как работает

Смотри здесь
Автор: remark
Дата: 07.02.07
. Там можно много значений.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[2]: Невероятно, но факт! Не константные значения в компай
От: remark Россия http://www.1024cores.net/
Дата: 06.02.07 22:36
Оценка:
Здравствуйте, IROV.., Вы писали:

IRO>Будь проще и люди подтянутся

А зачем они мне?

IRO>А то что только первое и второе вхождение будет, видно из кода, точнее из за механизма!


Да, тут я привёл только для двух, что бы было понятнее. Смотри здесь
Автор: remark
Дата: 07.02.07
для полной версии счётчика.

IRO>Вся ботва в двух ключевых моментах а именно


IRO>char engine( ... ); // выбор данной функции при перегрузке всегда последния (скока можно красивых вещей на этом сделать)


Не понял, что имеется в виду...
Да, engine(...), имеет наименьший приоритет при выборе перегрузок, на этом всё и построено...

IRO>и то что sizeof() не ищет реализацию а только оглавление.


Опять не понял...

IRO>Комеаут скорее всего не работает так как надо, потомучто он не успел найти обявление friend int engine(_true);


Да, Комеаут не работает, я знаю, но это не ко мне, а к нему


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[3]: Невероятно, но факт! Не константные значения в компай
От: IROV..  
Дата: 06.02.07 22:40
Оценка: +1
Здравствуйте, remark, Вы писали:

R>Здравствуйте, IROV.., Вы писали:


IRO>>Будь проще и люди подтянутся

R>А зачем они мне?
А в прочем ты прав _люди_ такое не используют


IRO>>А то что только первое и второе вхождение будет, видно из кода, точнее из за механизма!


R>Да, тут я привёл только для двух, что бы было понятнее. Смотри здесь
Автор: remark
Дата: 07.02.07
для полной версии счётчика.


полной?

error C2065: 'lin_gen' : undeclared identifier

я не волшебник, я только учусь!
Re[4]: Невероятно, но факт! Не константные значения в компай
От: remark Россия http://www.1024cores.net/
Дата: 06.02.07 22:52
Оценка:
Здравствуйте, IROV.., Вы писали:

IRO>Здравствуйте, remark, Вы писали:


R>>Здравствуйте, IROV.., Вы писали:


IRO>>>Будь проще и люди подтянутся

R>>А зачем они мне?
IRO>А в прочем ты прав _люди_ такое не используют
Я никого не принуждаю это использовать, и даже читать не принуждаю...


IRO>>>А то что только первое и второе вхождение будет, видно из кода, точнее из за механизма!


R>>Да, тут я привёл только для двух, что бы было понятнее. Смотри здесь
Автор: remark
Дата: 07.02.07
для полной версии счётчика.


IRO>полной?

IRO>

error C2065: 'lin_gen' : undeclared identifier


А ты скопировал определение lin_gen?


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[3]: Невероятно, но факт! Не константные значения в компай
От: night beast СССР  
Дата: 07.02.07 07:57
Оценка: +1
Здравствуйте, remark, Вы писали:

АТ>>На Comeau Online — не работает. А именно, оба 'magic<>::val' одинаковы.


R>Да, я знаю... это не ко мне, это к ним


ты им багрепорт отправил?
Re[3]: Невероятно, но факт! Не константные значения в компай
От: Аноним  
Дата: 07.02.07 08:14
Оценка: :)
Т.е. поиск имен, по-твоему, зависит от того, сколько ты раз написал magic<> ?
Или я не так тебя понял?
Re: Невероятно, но факт! Не константные значения в компайл-т
От: np9mi7 Россия  
Дата: 07.02.07 09:47
Оценка:
Здравствуйте, remark, Вы писали:

R>3. ... не знаю как назвать... легче на примере показать. Допустим есть класс:


R>В реальности ещё приходится делать поправку на выравнивание, но это уже детали.


Каким образом делается поправка на выравнивание?
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
"В любое мгновение принятия решения, лучшее, что вы можете сделать, это принять правильное решение; следующим лучшим вариантом будет принять неправильное решение, худший вариант – не принимать решения совсем" (c) Теодор Рузвельт.
Re[4]: Невероятно, но факт! Не константные значения в компай
От: night beast СССР  
Дата: 07.02.07 10:02
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Т.е. поиск имен, по-твоему, зависит от того, сколько ты раз написал magic<> ?


поиск имен зависит от того, какие аргументы принимает функция.
если функия принимает аргументом magic, то поиск подходящей функции будет происходит и в пространстве magic.

до инстанцирования magic<-1> есть только engine(...);
после добавляется engine(magic<-1>);

А>Или я не так тебя понял?


похоже на то.
Re[5]: Невероятно, но факт! Не константные значения в компай
От: Аноним  
Дата: 07.02.07 10:12
Оценка:
Здравствуйте, night beast, Вы писали:

NB>Здравствуйте, Аноним, Вы писали:


А>>Т.е. поиск имен, по-твоему, зависит от того, сколько ты раз написал magic<> ?


NB>поиск имен зависит от того, какие аргументы принимает функция.

NB>если функия принимает аргументом magic, то поиск подходящей функции будет происходит и в пространстве magic.

NB>до инстанцирования magic<-1> есть только engine(...);

NB>после добавляется engine(magic<-1>);

А ты не думал, что поиск имен не зависит от инстанцирования и вещи это ортогональные?

А>>Или я не так тебя понял?


NB>похоже на то.

Ну так поясни, почему похоже. Т.е. по твоим представлениям, С++ это язык в котором

void fun()
{
name;
name;
}

поиск имени name зависит от того, в какой оно строке стоит?
Re: Невероятно, но факт! Не константные значения в компайл-т
От: Константин Л.  
Дата: 07.02.07 10:14
Оценка: +1
Здравствуйте, remark, Вы писали:

[]

R>
R>char engine(...);

R>template<typename, int> struct magic;
R>typedef magic<char, -1> magicc;

R>template<typename type = int, int id = sizeof(engine(*(magicc*)0, *(type*)0))>
R>struct magic
R>{
R>    friend int engine(magicc&, type&);
R>    static const int val = id;
R>};

R>int main()
R>{
R>    char a[magic<>::val != magic<>::val ? 1 : -1];
R>}
R>


[]

как я понял каждое выражение magic<>::val должно инстанциировать magic заново и sizeof(engine(*(magicc*)0, *(type*)0)) выдавать новые значение?
Все равно не очень понятно
Re: Невероятно, но факт! Не константные значения в компайл-т
От: greenya Украина  
Дата: 07.02.07 10:26
Оценка:
Здравствуйте, remark, Вы писали:
.......
R>


м-да.
а я думал я знаю си++
Re[2]: Невероятно, но факт! Не константные значения в компай
От: Lorenzo_LAMAS  
Дата: 07.02.07 10:33
Оценка: +2
Здравствуйте, greenya, Вы писали:

G>Здравствуйте, remark, Вы писали:

G>.......
R>>


G>м-да.

G>а я думал я знаю си++

Тут речь идет о специфических глюканах компиляторов, к С++ не имеющих отношения, так что не расстраивайся.
Of course, the code must be complete enough to compile and link.
Re: Невероятно, но факт! Не константные значения в компайл-т
От: Pavel Chikulaev Россия  
Дата: 07.02.07 10:53
Оценка:
Здравствуйте, remark, Вы писали:

[snipped]

Смотри ветку http://rsdn.ru/Forum/?mid=1653980&amp;flat=0
Автор: Pavel Chikulaev
Дата: 01.02.06
Re[3]: Невероятно, но факт! Не константные значения в компай
От: Шахтер Интернет  
Дата: 07.02.07 11:05
Оценка:
Здравствуйте, remark, Вы писали:

R>Здравствуйте, Андрей Тарасевич, Вы писали:


АТ>>Хм... Оригинально, но не понятно, должно ли это работать так, как задумано. Как известно, friend declaration не вводит в охватывающий namespace видимого имени. Поэтому не ясно, почему в твоем случае при второй инстанциации 'magic' lookup для дефолтного аргумента вдруг должно найти именно "друга", а не явно объявленную оригинальную версию 'engine'...


R>Да, friend declaration не вводит в охватывающий namespace видимого имени. Всё правильно. Над этой проблемой пришлось побороться.

R>На помощь пришёл ADL! Смотри:

R>
R>class complex
R>{
R>  ...
R>  friend complex operator + (complex, complex);
R>};

R>complex c1, c2;
R>c1 + c2;
R>


R>Это же работает, хотя тоже вроде как не должно, т.к. operator + () вообще говоря не виден вне класса.


R>Поэтому я добавил в функцию engine() параметр magic&! Теперь она тоже попадает в рассмотрение!


С чего бы это? У тебя тип параметра не совпадает с типом, в котором продекларирован engine().
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[3]: Невероятно, но факт! Не константные значения в компай
От: Roman Odaisky Украина  
Дата: 07.02.07 11:47
Оценка:
Здравствуйте, remark, Вы писали:

RO>>Я попробовал на VC9, только первые 2 значения разные


R>Во-вторых, что такое VC9


sorry, VC8
До последнего не верил в пирамиду Лебедева.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.