Простенький компайл-тайм счетчик
От: rg45 СССР  
Дата: 28.03.17 22:08
Оценка: 112 (8)
Между делом придумался вот компайл-тайм счетчик. При всей простоте допускает одновременное использование нескольких независимых счетчиков. Также позволяет настраивать точку отчсета и шаг.

http://ideone.com/gmtlyE

template <int ORIGIN = 0, int INCREMENT = 1>
struct CTCOrigin : std::integral_constant<int, ORIGIN>
{
   static constexpr int increment = INCREMENT;
};

#define CREATE_CTC(name, ...) \
   template <size_t N> struct name : name<N - 1> { }; \
   template <> struct name<__LINE__> : CTCOrigin<__VA_ARGS__> { };

#define UPDATE_CTC(name) \
   template <> struct name<__LINE__> : name<__LINE__ - 1> \
   { \
      static constexpr int value = name<__LINE__ - 1>::value + increment; \
   };

#define GET_CTC(name) \
   name<__LINE__>::value
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[2]: Простенький компайл-тайм счетчик
От: Кодт Россия  
Дата: 30.03.17 10:06
Оценка: 21 (1) :)
Здравствуйте, jazzer, Вы писали:

J>Я сделал в свое время похожую штуку — и напоролся на ограничение рекурсии инстанцирования для сколько-нибудь больших файлов. Но это было давно, может, сейчас это и не проблема уже...


Вместо линейной рекурсии можно сделать деревянную!

http://ideone.com/E7VreH
template<int I> struct int_ { static constexpr int value = I; };
using int_0 = int_<0>;
using int_1 = int_<1>;

template<int I> struct point : int_0 {};
#define TAG_IT() template<> struct point<__COUNTER__> : int_1 {};

template<int L, int N> struct sumpoints;
template<int L, int H, int N> using splitsum = int_< sumpoints<L,H>::value + sumpoints<L+H,N-H>::value >;

template<int L> struct sumpoints<L, 0> : int_0 {};
template<int L> struct sumpoints<L, 1> : point<L> {};
template<int L, int N> struct sumpoints : splitsum<L, N/2, N> {};
    // лучше разбивать не пополам, а по старшему биту, - это даст меньше сочетаний <L,N> при массовом использовании
    // и, соответственно, меньше воплощений шаблона
    // но я сходу не вспомнил формулу для его нахождения

#define GET_IT() (sumpoints<0, __COUNTER__>::value)

constexpr int x = GET_IT();  // __COUNTER__ = 1, x = 0
constexpr int y = GET_IT();  // __COUNTER__ = 2, y = 0
TAG_IT();                    // __COUNTER__ = 3
TAG_IT();                    // __COUNTER__ = 4
TAG_IT();                    // __COUNTER__ = 5
constexpr int z = GET_IT();  // __COUNTER__ = 6, z = 3
Перекуём баги на фичи!
Отредактировано 30.03.2017 10:09 Кодт . Предыдущая версия .
Re[3]: Простенький компайл-тайм счетчик
От: Кодт Россия  
Дата: 29.03.17 10:28
Оценка: 9 (1) +1
Здравствуйте, rg45, Вы писали:

К>>1. Засоряет пространство типов — шаблоны воплощаются для каждого номера строки. Лучше попробовать пересадить на __COUNTER__.


R>С использованием __COUNTER__ — это мой текущий подход. Он обладает серьезнейшим ограничением — не позволяет иметь одновременно несколько независимых счетчиков. Это невероятно усложняет средства декларативного описания иерархических структур.

R>Кроме того, __COUNTER__, в отличие от __LINE__, может давать разнные последовательности чисел при включении в разные единицы трансляции и поэтому требует дополнительных телодвижений для соблюдения ODR.

Я имел в виду, что ты свои CTC перепишешь, используя внутри __COUNTER__ вместо __LINE__.
Для __COUNTER__ гарантируется монотонность и уникальность в пределах одной единицы трансляции, невзирая на старания препроцессора по склеиванию из инклудов и макросов.

А чтобы соблюсти ODR, — для этого же есть анонимные пространства имён?
Просто заверни определения своих шаблонов в namespace{....} — и всё.
Перекуём баги на фичи!
Re[2]: Улучшение (?)
От: night beast СССР  
Дата: 29.03.17 08:25
Оценка: 8 (1) -1
Здравствуйте, rg45, Вы писали:

R>Хотя, серьезным недостатком это подхода является невозможность модификации счетчиков внутри классов. Да и при использовании в разных пространствах имен тоже все грустно. Надо, помозговать, возможно, придумается какой-нибудь компромисс.


имхо, опасно это. от порядка подключения хидеров зависит.
Re[5]: Версия с __COUNTER__
От: Кодт Россия  
Дата: 29.03.17 12:55
Оценка: 18 (1)
Здравствуйте, rg45, Вы писали:

R>Спасибо за дельные советы. Переделал с учетом замечаний. Для соблюдения ODR, вместо анонимных пространств имен, использовал несколько другой подход, который позволяет использовать счетчики в области видимости классов. Просто в момент создания счетчика защщелкиваем значение __COUNTER__ в константу (counter_base) и отнимаем ее при всех последуюющих обращениях к __COUNTER__.


Вот это и будет нарушением одр.
Защёлкивать __COUNTER__ можно только в местную константу, ибо!

Но можно скрестить подходы: http://ideone.com/boAA3i
#include <iostream>
using namespace std;
#define DEF_BASE(name)       namespace { constexpr int name##localbase = __COUNTER__; }
#define GET_BASE(name)       (name##localbase)
#define GET_OFFSET(name)     GET_OFFSET_C(name,__COUNTER__)
#define GET_OFFSET_C(name,C) ((C)-GET_BASE(name))


#define BODY(V) { static constexpr int value = (V); }

#define START(name) \
  DEF_BASE(name) \
  template<int offset> struct name                      BODY(name<offset-1>::value); \
  template<>           struct name<0>                   BODY(0);

#define GET(name) (name<GET_OFFSET(name)>::value)

#define NEXT(name) NEXT_C(name, __COUNTER__)  // нам понадобится одно значение дважды, поэтому опосредуем
#define NEXT_C(name, C) \
  template<>           struct name<GET_OFFSET_C(name,C)>  BODY(name<GET_OFFSET_C(name,C)-1>::value + 1);

const int c0 = (__COUNTER__, __COUNTER__);
START(foo)
const int x = GET(foo);
const int c1 = (__COUNTER__, __COUNTER__);
const int y = GET(foo);
const int c2 = (__COUNTER__, __COUNTER__);
NEXT(foo)
const int z = GET(foo);
const int c3 = (__COUNTER__, __COUNTER__);
const int t = GET(foo);

int main() {
    cout << c0 << endl;
    cout << x << endl;
    cout << c1 << endl;
    cout << y << endl;
    cout << c2 << endl;
    cout << z << endl;
    cout << c3 << endl;
    cout << t << endl;
}
Перекуём баги на фичи!
Re[5]: Улучшение (?)
От: rg45 СССР  
Дата: 29.03.17 09:41
Оценка: 1 (1)
Здравствуйте, night beast, Вы писали:

R>>[Upd]

R>>Возможно, ты имеешь в виду использование одних и тех же счетчиков в разных заголовочных файлах? Да, с этим, конечно же проблема. Нот э таргет сценарио, как говорится

NB>да. речь об этом.

NB>если счетчик используется только во одном заголовочном файле, то нужен ли он вообще?

Ну это уже зависит от индивидуальных потребностей, лично мне эти счетчики имено для этого и нужны. Я их использую для декларативного описания больших структур данных (XML, например, и др.) как счетчики классов и полей. При этом счетчки, в большинстве своем, создаются в области видимости класса. А "размазывать" класс по нескольким заголовкам у меня нет необходимости
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[2]: Простенький компайл-тайм счетчик
От: rg45 СССР  
Дата: 29.03.17 10:05
Оценка: +1
Здравствуйте, Кодт, Вы писали:

К>А вообще, прикольный способ, по сравнению с более старым трюком на контекстно-зависимом выборе наилучшей специализации.

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

Ты имеешь ввиду Компайл-тайм счетчик remark'а
Автор: remark
Дата: 07.02.07
?
--
Не можешь достичь желаемого — пожелай достигнутого.
Re: Простенький компайл-тайм счетчик
От: jazzer Россия Skype: enerjazzer
Дата: 29.03.17 14:28
Оценка: +1
Здравствуйте, rg45, Вы писали:

R>Между делом придумался вот компайл-тайм счетчик. При всей простоте допускает одновременное использование нескольких независимых счетчиков. Также позволяет настраивать точку отчсета и шаг.


Я сделал в свое время похожую штуку — и напоролся на ограничение рекурсии инстанцирования для сколько-нибудь больших файлов. Но это было давно, может, сейчас это и не проблема уже...
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[3]: Простенький компайл-тайм счетчик
От: jazzer Россия Skype: enerjazzer
Дата: 29.03.17 14:38
Оценка: +1
Здравствуйте, rg45, Вы писали:

J>>Я сделал в свое время похожую штуку — и напоролся на ограничение рекурсии инстанцирования для сколько-нибудь больших файлов. Но это было давно, может, сейчас это и не проблема уже...


R>Сейчас это тоже проблема. Я вот буквально вчера мерял на 2015-й студии — максимально допустимая глубина такой рекурсии — 495. Наверное, этим можно как-то управлять, но предел будет в любом случае.


Да, тут, разве что, вставлять промежуточные UPDATE, когда такая ошибка вылазит. Или какой-нибудь
#define TOUCH_CTC(name) \
   template <> struct name<__LINE__> : name<__LINE__ - 1> \
   { \
      static constexpr int value = name<__LINE__ - 1>::value; \
   };

который не изменит значения счетчика
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[4]: Простенький компайл-тайм счетчик
От: rg45 СССР  
Дата: 29.03.17 21:05
Оценка: +1
Здравствуйте, jazzer, Вы писали:

J>Да, тут, разве что, вставлять промежуточные UPDATE, когда такая ошибка вылазит. Или какой-нибудь

J>
J>#define TOUCH_CTC(name) \
J>   template <> struct name<__LINE__> : name<__LINE__ - 1> \
J>   { \
J>      static constexpr int value = name<__LINE__ - 1>::value; \
J>   };
J>

J>который не изменит значения счетчика

Так ведь и того проще — нет даже необходимости явно определять value, проблема решается простым наследованием:

#define TOUCH_CTC(name) \
   template <> struct name<__LINE__> : name<__LINE__ - 1> { };


В то же время я думаю, что эта проблема не будет возникать слишком часто при разумном подходе к структурированию программного кода.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re: CTC без препроцссора - помогите довести до ума!
От: kov_serg Россия  
Дата: 14.04.17 09:52
Оценка: +1
Здравствуйте, rg45, Вы писали:

Лучше раскажите где вы используете compile time counter ?
Re: Улучшение (?)
От: rg45 СССР  
Дата: 29.03.17 07:48
Оценка:
Здравствуйте, rg45, Вы писали:

Улучшение состоит в том, что теперь не требуется процедуры создания счетчика как таковой — счетчиком с любым произвольным именем можно сразу брать и пользоваться при помощи макроса GET_CTC и наращивать при помощи UPDATE_CTC. По умолчанию все счетчики стартуют с нуля с шагом 1. Если нужно запустить счетчик от другой точки отсчета и/или с другим шагом, можно воспользоваться опциональным макросом RESET_CTC. Этим же макросом можно перезапустить счетчик, уже находившийся в использовании.

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

http://ideone.com/HZQ6us

template <int ORIGIN = 0, int INCREMENT = 1>
struct $CTCOrigin : std::integral_constant<int, ORIGIN>
{
   static constexpr int increment = INCREMENT;
};
 
template <typename Tag, size_t LINE>
struct $CTC : $CTC<Tag, LINE - 1> { };
 
template <typename Tag>
struct $CTC<Tag, 0> : $CTCOrigin<0, 1> { };
 
#define RESET_CTC(Tag, ...) \
   template <> struct $CTC<class Tag, __LINE__> : $CTCOrigin<__VA_ARGS__> { };
 
#define UPDATE_CTC(Tag) \
   template <> struct $CTC<class Tag, __LINE__> : $CTC<class Tag, __LINE__ - 1> \
   { \
      static constexpr int value = $CTC<class Tag, __LINE__ - 1>::value + increment; \
   };
 
#define GET_CTC(Tag) \
   $CTC<class Tag, __LINE__ - 1>::value
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 29.03.2017 8:13 rg45 . Предыдущая версия . Еще …
Отредактировано 29.03.2017 8:13 rg45 . Предыдущая версия .
Отредактировано 29.03.2017 8:09 rg45 . Предыдущая версия .
Re[3]: Улучшение (?)
От: rg45 СССР  
Дата: 29.03.17 08:40
Оценка:
Здравствуйте, night beast, Вы писали:

R>>Хотя, серьезным недостатком это подхода является невозможность модификации счетчиков внутри классов. Да и при использовании в разных пространствах имен тоже все грустно. Надо, помозговать, возможно, придумается какой-нибудь компромисс.


NB>имхо, опасно это. от порядка подключения хидеров зависит.


Пока я не совсем понимаю, в чем опасность. Да и зависимости от порядка подключения тоже пока не вижу. Значение __LINE__ от порядка подключения не зависит, так что с ODR все в продяке, вроде бы. Так где же грабли?

[Upd]
Возможно, ты имеешь в виду использование одних и тех же счетчиков в разных заголовочных файлах? Да, с этим, конечно же проблема. Нот э таргет сценарио, как говорится
И да, это весомый аргумет в пользу явного создания счетчиков — компилятор убережет от случайных ошибок.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 29.03.2017 8:55 rg45 . Предыдущая версия . Еще …
Отредактировано 29.03.2017 8:47 rg45 . Предыдущая версия .
Re[4]: Улучшение (?)
От: night beast СССР  
Дата: 29.03.17 09:31
Оценка:
Здравствуйте, rg45, Вы писали:

R>[Upd]

R>Возможно, ты имеешь в виду использование одних и тех же счетчиков в разных заголовочных файлах? Да, с этим, конечно же проблема. Нот э таргет сценарио, как говорится

да. речь об этом.
если счетчик используется только во одном заголовочном файле, то нужен ли он вообще?
Re: Простенький компайл-тайм счетчик
От: Кодт Россия  
Дата: 29.03.17 09:37
Оценка:
Здравствуйте, rg45, Вы писали:

R>Между делом придумался вот компайл-тайм счетчик. При всей простоте допускает одновременное использование нескольких независимых счетчиков. Также позволяет настраивать точку отчсета и шаг.


1. Засоряет пространство типов — шаблоны воплощаются для каждого номера строки. Лучше попробовать пересадить на __COUNTER__.
2. Если __LINE__ проваливается в рантайм (микрософтовский режим сборки "edit&continue") — будут проблемы. См. выше

А вообще, прикольный способ, по сравнению с более старым трюком на контекстно-зависимом выборе наилучшей специализации.
(Сейчас на память не воспроизведу, как там это было устроено).
Перекуём баги на фичи!
Re[2]: Простенький компайл-тайм счетчик
От: rg45 СССР  
Дата: 29.03.17 09:45
Оценка:
Здравствуйте, Кодт, Вы писали:

К>1. Засоряет пространство типов — шаблоны воплощаются для каждого номера строки. Лучше попробовать пересадить на __COUNTER__.


С использованием __COUNTER__ — это мой текущий подход. Он обладает серьезнейшим ограничением — не позволяет иметь одновременно несколько независимых счетчиков. Это невероятно усложняет средства декларативного описания иерархических структур.

[Upd]
Кроме того, __COUNTER__, в отличие от __LINE__, может давать разнные последовательности чисел при включении в разные единицы трансляции и поэтому требует дополнительных телодвижений для соблюдения ODR.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 29.03.2017 9:55 rg45 . Предыдущая версия .
Re[4]: Улучшение (?)
От: Кодт Россия  
Дата: 29.03.17 10:19
Оценка:
Здравствуйте, rg45, Вы писали:

R>Пока я не совсем понимаю, в чем опасность. Да и зависимости от порядка подключения тоже пока не вижу. Значение __LINE__ от порядка подключения не зависит, так что с ODR все в продяке, вроде бы. Так где же грабли?


R>[Upd]

R>Возможно, ты имеешь в виду использование одних и тех же счетчиков в разных заголовочных файлах? Да, с этим, конечно же проблема. Нот э таргет сценарио, как говорится
R>И да, это весомый аргумет в пользу явного создания счетчиков — компилятор убережет от случайных ошибок.

Проблема в том, что с помощью include можно нарушать монотонность значений __LINE__ в рамках одной единицы трансляции.
// setup.h
CREATE_CTC(foo, 1000)

// some.h
const int x = GET_CTC(foo); // __LINE__ = 2 --> 1000
UPDATE_CTC(foo)             // __LINE__ = 3 --> 1001
const int y = GET_CTC(foo); // __LINE__ = 4 --> 1001

// some.cc - хороший
#include "setup.h"
#include "some.h"
.....
UPDATE_CTC(foo)             // __LINE__ = 101 --> 1002
const int z = GET_CTC(foo); // __LINE__ = 102 --> 1002
const int t = GET_CTC(foo); // __LINE__ = 103 --> 1002


// some.cc - плохой
#include "setup.h"
.....
UPDATE_CTC(foo)             // __LINE__ = 100 --> 1001
const int z = GET_CTC(foo); // __LINE__ = 101 --> 1001
#include "some.h"           // ошибка компиляции - специализация после использования
const int t = GET_CTC(foo); // __LINE__ = 103


Вторая проблема — в том, что с помощью макросов можно нарушать уникальность значений __LINE__
#define BATCH_UPDATE(x,y) \
  const int x = GET_CTC(foo) /* foo<L> */ \
  UPDATE_CTC(foo) /* foo<L> : foo<L> + 1 */ \
  const int y = GET_CTC(foo) /* foo<L> */ \
  //endmacro

BATCH_UPDATE(xxx, yyy)  // ошибка компиляции - специализация после использования
Перекуём баги на фичи!
Re[4]: Версия с __COUNTER__
От: rg45 СССР  
Дата: 29.03.17 12:14
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Я имел в виду, что ты свои CTC перепишешь, используя внутри __COUNTER__ вместо __LINE__.

К>Для __COUNTER__ гарантируется монотонность и уникальность в пределах одной единицы трансляции, невзирая на старания препроцессора по склеиванию из инклудов и макросов.
К>А чтобы соблюсти ODR, — для этого же есть анонимные пространства имён?
К>Просто заверни определения своих шаблонов в namespace{....} — и всё.

Спасибо за дельные советы. Переделал с учетом замечаний. Для соблюдения ODR, вместо анонимных пространств имен, использовал несколько другой подход, который позволяет использовать счетчики в области видимости классов. Просто в момент создания счетчика защщелкиваем значение __COUNTER__ в константу (counter_base) и отнимаем ее при всех последуюющих обращениях к __COUNTER__.

http://ideone.com/yyL735

template <int ORIGIN = 0, int INCREMENT = 1>
struct $CTCOrigin : std::integral_constant<int, ORIGIN>
{
   static constexpr int increment = INCREMENT;
};

#define CREATE_CTC(name, ...) \
   template <size_t N> struct name : name<N - 1> { }; \
   template <> struct name<0> : $CTCOrigin<__VA_ARGS__> \
   { \
      static constexpr int counter_base = __COUNTER__; \
   };

#define UPDATE_CTC_(name, counter) \
   template <> struct name<counter - name<0>::counter_base> \
   : name<counter - name<0>::counter_base - 1> \
   { \
      using base = name<counter - name<0>::counter_base - 1>; \
      static constexpr int value = base::value + increment; \
   };
#define UPDATE_CTC(name) UPDATE_CTC_(name, __COUNTER__)

#define GET_CTC(name) \
   name<__COUNTER__ - name<0>::counter_base>::value
--
Не можешь достичь желаемого — пожелай достигнутого.
Re: Простенький компайл-тайм счетчик
От: Goodhope  
Дата: 29.03.17 12:41
Оценка:
Здравствуйте, rg45, Вы писали:

Прошу прощения за глупый вопрос — а зачем это может быть нужно?
Re[6]: Версия с __COUNTER__
От: rg45 СССР  
Дата: 29.03.17 13:23
Оценка:
Здравствуйте, Кодт, Вы писали:

R>>Спасибо за дельные советы. Переделал с учетом замечаний. Для соблюдения ODR, вместо анонимных пространств имен, использовал несколько другой подход, который позволяет использовать счетчики в области видимости классов. Просто в момент создания счетчика защщелкиваем значение __COUNTER__ в константу (counter_base) и отнимаем ее при всех последуюющих обращениях к __COUNTER__.


К>Вот это и будет нарушением одр.

К>Защёлкивать __COUNTER__ можно только в местную константу, ибо!

Не будет! Ибо константы и так имеют внутреннее связывание по умолчанию. Правда, это не касается констант-членов класса Но это не так уж сложно разрулить. Уже в процессе...
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[2]: Простенький компайл-тайм счетчик
От: rg45 СССР  
Дата: 29.03.17 13:34
Оценка:
Здравствуйте, Goodhope, Вы писали:

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


G>Прошу прощения за глупый вопрос — а зачем это может быть нужно?


В двух словах ответить даже как-то затрудняюсь, думаю применений можно придумать множество самых разных. Мне эти счетчики помогают заставить компилятор писать программы вместо меня
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[7]: Версия с __COUNTER__
От: Кодт Россия  
Дата: 29.03.17 13:36
Оценка:
Здравствуйте, rg45, Вы писали:

К>>Вот это и будет нарушением одр.

К>>Защёлкивать __COUNTER__ можно только в местную константу, ибо!

R>Не будет! Ибо константы и так имеют внутреннее связывание по умолчанию. Правда, это не касается констант-членов класса Но это не так уж сложно разрулить. Уже в процессе...


Ну так именно это я и сделал: вытащил защёлку из членов в свободные.
Перекуём баги на фичи!
Re[2]: Простенький компайл-тайм счетчик
От: Vain Россия google.ru
Дата: 29.03.17 13:40
Оценка:
Здравствуйте, Goodhope, Вы писали:

G>Прошу прощения за глупый вопрос — а зачем это может быть нужно?

Ну обычно для формирования всяких табличных монотонных имен и значений по типу енума, но с произвольным шагом.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[8]: Версия с __COUNTER__
От: rg45 СССР  
Дата: 29.03.17 14:17
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Ну так именно это я и сделал: вытащил защёлку из членов в свободные.


Идея, конечно, понятна но пользоваться этим не очень удобно. Создавая счетчик в скопе класса, нужно не забыть определить для него вспомогательную сущность в пространсте имен. Простота и элеганость сразу же страдают, а следовательно и надежность тоже. А между тем, нам ведь не нужно заводить базу для каждого счетчика в отдельности — достаточно одной на заголовочный файл. Меня __LINE__ потому и привлекла, что именно этой проблемы она не создавала (а проблем перечисленных выше просто не возникало при моих сценариях использования). Как водится, приходим к тому, что идеального варианта не существует, чем-то нужно пожертвовать?
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[2]: Простенький компайл-тайм счетчик
От: rg45 СССР  
Дата: 29.03.17 14:33
Оценка:
Здравствуйте, jazzer, Вы писали:

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


J>Я сделал в свое время похожую штуку — и напоролся на ограничение рекурсии инстанцирования для сколько-нибудь больших файлов. Но это было давно, может, сейчас это и не проблема уже...


Сейчас это тоже проблема. Я вот буквально вчера мерял на 2015-й студии — максимально допустимая глубина такой рекурсии — 495. Наверное, этим можно как-то управлять, но предел будет в любом случае.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[3]: Простенький компайл-тайм счетчик
От: Кодт Россия  
Дата: 30.03.17 09:23
Оценка:
Здравствуйте, rg45, Вы писали:

J>>Я сделал в свое время похожую штуку — и напоролся на ограничение рекурсии инстанцирования для сколько-нибудь больших файлов. Но это было давно, может, сейчас это и не проблема уже...

R>Сейчас это тоже проблема. Я вот буквально вчера мерял на 2015-й студии — максимально допустимая глубина такой рекурсии — 495. Наверное, этим можно как-то управлять, но предел будет в любом случае.

Управляется каким-то ключом (я, помню, игрался с этим давно).
Вот именно поэтому __COUNTER__ предпочтительнее __LINE__. Особенно, если мало кто будет им пользоваться.
Перекуём баги на фичи!
Re[3]: Простенький компайл-тайм счетчик
От: Кодт Россия  
Дата: 30.03.17 11:29
Оценка:
К> // лучше разбивать не пополам, а по старшему биту, — это даст меньше сочетаний <L,N> при массовом использовании
К> // и, соответственно, меньше воплощений шаблона
К> // но я сходу не вспомнил формулу для его нахождения

Наверно, можно вот такое
constexpr int pivot(unsigned N) {
  int r = N & (N-1); // обнуление младшего бита
  return r ? r : N/2;
}

То есть, мы просто с другого направления заедем при построении поразрядного дерева.
Например, нам надо взять суммы <0,7>, <0,11> и <0,12>.

Дихотомией
0,7 =  0,3             + 3,4
       0,1 + 1,2         3,2       + 5,2
             1,1 + 2,1   3,1 + 4,1   5,1 + 6,1

0,11 = 0,5                   + 5,6
       0,2       + 2,3         5,3             + 8,3
       0,1 + 1,1   2,1 + 3,2   5,1 + 6,2         8,1 + 9,2
       ===   ===   ===   ###         6,1 + 7,1         9,1 + 10,1
                                     ===

0,12 = 0,6                   + 6,6
       0,3 + 3,3               6,3             + 9,3
       ###   3,1 + 4,2         6,1 + 7,2         9,1 + 10,2
             ===   4,1 + 5,1   ===   7,1 + 8,1   ===   10,1 + 11,1
                   ===   ===         ===   ===         ====

=== — повторно использованные воплощения точек
### — повторно использованные воплощения сумм
Т.е. 0,7 породил 13 воплощений, 0,11 добавил 14, 0,12 добавил 10.

Разбивкой по старшему разряду
0,7 =  0,4                   + 4,3
       0,2       + 2,2         4,2       + 6,1
       0,1 + 1,1   2,1 + 3,1   4,1 + 5,1

0,11 = 0,8                   + 8,3
       0,4 + 4,4               8,2       + 10,1
       ###   4,2 + 6,2         8,1 + 9,1
             ###   6,1 + 7,1
                   ===

0,12 = 0,8 + 8,4
       ###   8,2 + 10,2
             ###   10,1 + 11,1
                   ====

Получилось 13 + 10 + 4.

Разбивкой по младшему разряду
0,7 =  0,6                               + 6,1
       0,4                   + 4,2
       0,2       + 2,2         4,1 + 5,1
       0,1 + 1,1   2,1 + 3,1

0,11 = 0,10                              + 10,1
       0,8                   + 8,2
       0,4 + 4,4               8,1 + 9,1
       ###   4,2 + 6,2
             ###   6,1 + 7,1
                   ===

0,12 = 0,10 + 10,2
       ####   10,1 + 11,1
              ====

Получилось 13 + 10 + 3.
Перекуём баги на фичи!
CTC без препроцссора - помогите довести до ума!
От: rg45 СССР  
Дата: 13.04.17 21:32
Оценка:
Это не готовая реализация CTC, а только прототип, упрощенный до предела так, чтобы видна была идея.

Достоинства:

Недостатки:


http://rextester.com/ZSP64839

constexpr size_t CTC_MAX = 498;

template <size_t i>
struct Index : Index<i - 1> { static constexpr size_t value = i; };
template <> struct Index<0> { static constexpr size_t value = 0; };

Index<0> generate(...);

#define CREATE_CTC(name) \
   template <typename T = decltype(generate(Index<CTC_MAX>()))> \
   struct name : T \
   { \
      friend Index<T::value + 1> generate(T); \
   };

#define GET_CTC(name) name<>::value

Пример использования:
CREATE_CTC(ctc1)

enum class Foo
{
   zero = GET_CTC(ctc1),
   one = GET_CTC(ctc1),
   two = GET_CTC(ctc1),
   three = GET_CTC(ctc1),
   four = GET_CTC(ctc1),
   five = GET_CTC(ctc1),
};
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 14.04.2017 8:16 rg45 . Предыдущая версия . Еще …
Отредактировано 13.04.2017 22:16 rg45 . Предыдущая версия .
Отредактировано 13.04.2017 21:53 rg45 . Предыдущая версия .
Отредактировано 13.04.2017 21:33 rg45 . Предыдущая версия .
Re[2]: CTC без препроцссора - помогите довести до ума!
От: rg45 СССР  
Дата: 14.04.17 10:05
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>Лучше раскажите где вы используете compile time counter ?


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

Абстрактно, чуть детальнее. Представь, есть шаблонное объявление каких-то свойств
template <size_t> struct MemeberTraits;


При помощи CTC мы можем определить семейство специализаций:

CREATE_CTC<MemberCounter>

template <>
struct MemeberTraits<GET_CTC<MemberCounter>> // MemeberTraits<0>
{
  //...
};
template <>
struct MemeberTraits<GET_CTC<MemberCounter>> // MemeberTraits<1>
{
  //...
};
template <>
struct MemeberTraits<GET_CTC<MemberCounter>> // MemeberTraits<2>
{
  //...
};


Внутри каждой из специализаций разместить набор статических свойств, специфичных, например, для членов какого-то класса. Или для элеметнов списка типов, или для перечисления, или для элементров интрузивного профайлера, или... После этого эти наборы специализаций можно использовать для авотматической генерации, например, классов сериализации-десериализации, конверторов, аксессоров к базам данных... В общем, дофига всего.

Этот подход позволяет сколько угодно добавлять новые структуры данных, изменять и расширять существующие, не меняя при этом процедур работы с этими данными (сериализация/десериализация, вычитка/запись из/в БД, ввод-вывод, конверторы и пр).
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 14.04.2017 10:53 rg45 . Предыдущая версия . Еще …
Отредактировано 14.04.2017 10:51 rg45 . Предыдущая версия .
Отредактировано 14.04.2017 10:39 rg45 . Предыдущая версия .
Отредактировано 14.04.2017 10:39 rg45 . Предыдущая версия .
Отредактировано 14.04.2017 10:35 rg45 . Предыдущая версия .
Отредактировано 14.04.2017 10:25 rg45 . Предыдущая версия .
Отредактировано 14.04.2017 10:09 rg45 . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.