Пришло время очередного ресёрча
В компайл-тайм все объекты константные. Будь то интегральные значения или типы. Т.е. имея запись:
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++
Дальше у кого на сколько хватит фантазии
Код указанных фич я приведу в отдельных постах, что бы сюда слишком много не мешать.