Генератор случайных чисел времени компиляции

Тема в разделе "LANGS.C", создана пользователем Rel, 26 май 2011.

  1. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    написал генератор псевдо-случайных чисел времени компиляции на основе рекурсивных шаблонов... в качестве зерна для рандомизатора хотел использовать время и дату компиляции, то есть символы в переменной препроцессора например: (__TIME__[0] - '0') и другие... но получаю ошибку о том, что элементы массива не могут быть использованы в константных выражениях... подскажите, как заставить препроцессор взять значение __TIME__[0], а не делать тупую символьную подстановку, и вообще возможно ли это? может быть есть какие-то альтернативы, которые я мог бы использовать в качестве зерна рандомизатора?

    чтобы было понятнее, приведу пример кода:
    Код (Text):
    1. // Пример определения генератора
    2. #define  COMPILETIME_RANDSEED (__TIME__[0] - '0')
    3. template <uint32_t N> struct   CompileTime_Random    { enum { Value = 100 + CompileTime_Random<N - 1>::Value }; };
    4. template <>           struct   CompileTime_Random<0> { enum { Value = COMPILETIME_RANDSEED }; };
    5. #define  COMPILETIME_RANDOM CompileTime_Random<__COUNTER__>::Value
    6.  
    7. // Пример использования
    8. printf("%d\n", COMPILETIME_RANDOM);
    9. printf("%d\n", COMPILETIME_RANDOM);
    10. printf("%d\n", COMPILETIME_RANDOM);
    ЗЫ необходимо решить средствами стандартного препроцессора
    ЗЗЫ будем внимательны, обсуждаем генератор псевдо-случайных чисел времени компиляции
    ЗЗЗЫ понятно, что всегда можно устанавливать зерно руками, но очень не хотелось бы этого делать, тк генерировать зерно относительно времени компиляции гораздо красивее и удобнее...
     
  2. Booster

    Booster New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2004
    Сообщения:
    4.860
    Возможно так?
    const int COMPILETIME_RANDSEED = __TIME__[0];
     
  3. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    я пробовал... в этом случае возвращается ошибка мол COMPILETIME_RANDSEED не может присутствовать в константном выражении... если задать COMPILETIME_RANDSEED каким-то константным значением, то все проходит на ура... хитрый компилятор мать его)
     
  4. Booster

    Booster New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2004
    Сообщения:
    4.860
    Пробовал на comeau и он схавал. Почему не может присутствовать? Бред какой-то.
     
  5. SadKo

    SadKo Владимир Садовников

    Публикаций:
    8
    Регистрация:
    4 июн 2007
    Сообщения:
    1.610
    Адрес:
    г. Санкт-Петербург
    А если двойную макроподстановку попробовать?
    Код (Text):
    1. #define __GEN_COMPILETIME_RANDSEED (__TIME__[0] - '0')
    2. #define  COMPILETIME_RANDSEED __GEN_COMPILETIME_RANDSEED
     
  6. AsmGuru62

    AsmGuru62 Member

    Публикаций:
    0
    Регистрация:
    12 сен 2002
    Сообщения:
    689
    Адрес:
    Toronto
    Можно также сделать утилиту, которая генерирует в run-time вот такой файл:
    Код (Text):
    1. #define  COMPILETIME_RANDSEED  <random value here>
    и затем перед каждой компиляцией запускать утилиту - в BAT пакете или там в
    Pre-Build Step - если через среду.
     
  7. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    ты уверен, что проделал именно тот самый тест)) например:
    Код (Text):
    1. const int Test1 = __TIME__[0];
    2. enum Test2 { value = Test1 };
    мингв отвечает:
    я пробовал, проблема в том, что препроцессор не хочет вычислять значение, а делает тупую текстовую подстановку:
    Код (Text):
    1. #define __GEN_COMPILETIME_RANDSEED (__TIME__[0] - '0')
    2. #define  COMPILETIME_RANDSEED __GEN_COMPILETIME_RANDSEED
    3. enum Test2 { value = COMPILETIME_RANDSEED };
    мингв отвечает:
    да это понятно... я хотел бы обойтись стандартными средствами... с тем же успехом можно свой препроцессор написать... кстати я никогда не пробовал прикручивать сторонние препроцессоры... как это сделать? я могу поставить его в пребилд-ивент, но куда генерировать файлы после обработки и как сделать чтобы затем их подцеплял компилятор? какие готовые препроцессоры существуют для плюсов? я только GPP знаю...
     
  8. qqwe

    qqwe New Member

    Публикаций:
    0
    Регистрация:
    2 янв 2009
    Сообщения:
    2.914
    вброс от меня

    Код (Text):
    1. #define SEED 11
    2. #define RND_N (*(long long*)__TIME__ % (long long)SEED)
     
  9. Booster

    Booster New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2004
    Сообщения:
    4.860
    Rel
    Попробуй сам - компилит нормально.

    Не подозревал что с __TIME__[0] будет отличие.
     
  10. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    да... но это на чисто на препроцессоре, там не сделаешь добротного алгоритма генерации псевдо-случайных чисел... это случайное число фактически генерируется не препроцессором, а оптимизатором компилятора... препроцессор только подставляет строку в код... для моего случая твоя идея похоже тоже не подходит, тк в константных выражениях не могут присутствовать касты типов и указатели сами по себе...

    никогда не пробовал comeau, хороший компилятор? как у него с поддержкой C++0x? и он платный вроде, не?
     
  11. qqwe

    qqwe New Member

    Публикаций:
    0
    Регистрация:
    2 янв 2009
    Сообщения:
    2.914
    Rel
    га? вы не поняли, но уже против?
    окончание ответа, начиная с подставления строки в код непонятно уже мне.

    пользоваться или нет - дело ваше. код дает константные случайные числа от 0 и до 63 разрядов.

    --
    (char)127 -- константа
    (void*)0 -- тоже константа

    // int foo(){printf("foo");}
    &foo -- тоже как ни странно константа
    (unsigned)&foo + 8 -- снова константа и в компиляте вы увидите ее как одно число

    не бойтесь С, он почти не кусается.
     
  12. Booster

    Booster New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2004
    Сообщения:
    4.860
    Rel
    Вообще-то шаблоны это то-же препроцессор, но слегка улучшенный. Так что qqwe прав, данную задачу вполне можно решить препроцессором. Но сдаётся мне что проблема с __TIME__, это какое-то надуманное ограничение компилятора для перечислений. Ничто не мешает использовать функции, главное хорошенько заинлайнить.

    Отличный, платный. С C++0x как и у всех, то-есть частично.
     
  13. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    дело не в си... я вряд ли смогу использовать ваше решение так как мне нужно...

    попробую уточнить... например, как вашим методом сделать так:
    Код (Text):
    1. printf("%d\n", RND_N); // Одно число
    2. printf("%d\n", RND_N); // Другое число
    3. printf("%d\n", RND_N); // Третье число
    макрос должен как-то себя менять...

    или например, есть определенное количество инлайн-функций, как подставить в код случайную из них:
    Код (Text):
    1. #define RND_FUNC(prefix, maxid) prefix_##(RND_N % (maxid + 1))
    не сработает, так как оператор ## исполняется раньше подстановки...
    я как бы ничего не имею против вашего решения, просто видимо для моей задачи оно не применимо...

    задачу решил с помощью нового стандарт C++0x и constexpr... для этого правда пришлось прокачать свою MinGW до GCC версии 4.6.0... но зато сейчас решение красивое и работает) спасибо всем за помощь, если есть еще идеи по теме или просто по вычислениям во время компиляции, с удовольствием их пообсуждаю с вами)
     
  14. qqwe

    qqwe New Member

    Публикаций:
    0
    Регистрация:
    2 янв 2009
    Сообщения:
    2.914
    Rel
    не знаю, это зависит от фантазии. например, так
    Код (Text):
    1. #define SEED 11
    2. #define RND_N(n) ((*(long long*)__TIME__ + ((n) << 16))% (long long)SEED)
    но вы, наверно, понимаете, что тк источник случайности 1, то такие числа будут псевдослучайными.
    трудно ответить, тк я не вполне понимаю чего вы этим хотите добиться. вообще не понимаю. например, < % (maxid + 1) > это возможная, но неправильная форма. делитель определяет только разрядность и разброс остатка. наилучший разброс дают простые делители, те делить надо на фиксированное, достаточно большое простое число.
    как решать вашу задачу - дело ваше. вам красиво, значит вам красиво. я же люблю универсальность и простоту.
     
  15. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    ну можно взять простой алгоритм из ANSI-C:
    Код (Text):
    1. randhold = 12345 + 1103515245 * randhold;
    2. return randhold;
    подскажите, как его переписать для генерации псевдослучайных чисел во время компиляции с помощью препроцессором?

    ну как объяснить... есть (maxid + 1) инлайн функций, допустим они имеют вид prefix_id (например func_0, func_1, ..., func_maxid)... необходимо в определенные места обычных функций подставить код случайно выбранной инлайн функции... этакий "морфинг" кода при компиляции... такими инлайн функциями могут быть допустим различные антиотладочные приемы, или какой-то хитрый асм-код делающий что-нибудь полезное или же бесполезное, смущая человека производящего анализ кода... и от компиляции к компиляции такие вставки будут различны...
     
  16. valterg

    valterg Active Member

    Публикаций:
    0
    Регистрация:
    19 авг 2004
    Сообщения:
    2.105
    Rel
    Вам же предложили решение: генерить исходники своей программой. Зачем создавать сложности.
     
  17. qqwe

    qqwe New Member

    Публикаций:
    0
    Регистрация:
    2 янв 2009
    Сообщения:
    2.914
    Rel
    я вот не пойму, чем вам так въелись эти псевдослучайные числа, если у вас есть настоящие случайные - дата и время компиляции + текущая строка вставки макроса? будьте проще.

    например,

    Код (Text):
    1. #define RND_N (unsigned)(((*(long long*)__TIME__ + ((long long)*(long*)&__TIME__[4] << 31)) ^ ((long long)__LINE__ << 21)) % (long long)SEED)
    и в каждой строке иное случайное число (единственно что, __TIME__ имеет шаг > 1 сек)

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

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

    Код (Text):
    1. #define KURDY_BURDY \
    2.     switch(RND_N){          \
    3.         case 1:             \
    4.             printf("1\n");  \
    5.             break;          \
    6.                             \
    7.         case 2:             \
    8.             printf("2\n");  \
    9.             break;          \
    10.                             \
    11.         // ....             \
    12.                             \
    13.         default:                \
    14.             printf("default\n");    \
    15.             break;          \
    16.                             \
    17.     }
    ьщжете попробовать повставлять в разных строках RND_N или KURDY_BURDY
     
  18. freyr

    freyr New Member

    Публикаций:
    0
    Регистрация:
    23 фев 2010
    Сообщения:
    95
    __COUNTER__ макрос подойдет, но он только c VS 10 вроде-бы...

    Код (Text):
    1. #include <stdio.h>
    2. #define FUNC2(x,y) x##y
    3. #define FUNC1(x,y) FUNC2(x,y)
    4. #define FUNC(x) FUNC1(x,__COUNTER__)
    5.  
    6. int FUNC(my_unique_prefix);
    7. int FUNC(my_unique_prefix);
    8.  
    9. int main() {
    10.    my_unique_prefix0 = 0;
    11.    printf_s("\n%d",my_unique_prefix0);
    12.    my_unique_prefix0++;
    13.    printf_s("\n%d",my_unique_prefix0);
    14. }
     
  19. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    каунтер макрос доступен в гцц с версии 4.3: http://gcc.gnu.org/gcc-4.3/changes.html
    как его ограничить модулем? пример:
    Код (Text):
    1. #define FUNC2(x,y) x##y
    2. #define FUNC1(x,y) FUNC2(x,y)
    3. #define FUNC(x) FUNC1(x,__COUNTER__ [b]% 4[/b]
    4. )
    5.  
    6. inline void func0() { printf("FUNC 0!\n"); }
    7. inline void func1() { printf("FUNC 1!\n"); }
    8. inline void func2() { printf("FUNC 2!\n"); }
    9. inline void func3() { printf("FUNC 3!\n"); }
    10.  
    11. int main()
    12. {
    13.     FUNC(func)();
    14.     FUNC(func)();
    15.     FUNC(func)();
    16.     FUNC(func)();
    17.     FUNC(func)();
    18.  
    19.     return 0;
    20. }
    __TIME__ - время начала компиляции, оно не меняется во время компиляции...

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

    да, идея со свитчем/ифами мне нравится...
     
  20. cppasm

    cppasm New Member

    Публикаций:
    0
    Регистрация:
    18 июл 2006
    Сообщения:
    923
    В GCC есть __attribute__((always_inline))