Простенький компайл-тайм счетчик
От: 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: Улучшение (?)
От: 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[2]: Улучшение (?)
От: night beast СССР  
Дата: 29.03.17 08:25
Оценка: 8 (1) -1
Здравствуйте, rg45, Вы писали:

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


имхо, опасно это. от порядка подключения хидеров зависит.
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[5]: Улучшение (?)
От: rg45 СССР  
Дата: 29.03.17 09:41
Оценка: 1 (1)
Здравствуйте, night beast, Вы писали:

R>>[Upd]

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

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

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

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

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


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

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

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

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

Ты имеешь ввиду Компайл-тайм счетчик remark'а
Автор: remark
Дата: 07.02.07
?
--
Не можешь достичь желаемого — пожелай достигнутого.
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[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[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[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[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: Простенький компайл-тайм счетчик
От: 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
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.