Между делом придумался вот компайл-тайм счетчик. При всей простоте допускает одновременное использование нескольких независимых счетчиков. Также позволяет настраивать точку отчсета и шаг.
Улучшение состоит в том, что теперь не требуется процедуры создания счетчика как таковой — счетчиком с любым произвольным именем можно сразу брать и пользоваться при помощи макроса GET_CTC и наращивать при помощи UPDATE_CTC. По умолчанию все счетчики стартуют с нуля с шагом 1. Если нужно запустить счетчик от другой точки отсчета и/или с другим шагом, можно воспользоваться опциональным макросом RESET_CTC. Этим же макросом можно перезапустить счетчик, уже находившийся в использовании.
Хотя, серьезным недостатком это подхода является невозможность модификации счетчиков внутри классов. Да и при использовании в разных пространствах имен тоже все грустно. Надо, помозговать, возможно, придумается какой-нибудь компромисс.
Здравствуйте, rg45, Вы писали:
R>Хотя, серьезным недостатком это подхода является невозможность модификации счетчиков внутри классов. Да и при использовании в разных пространствах имен тоже все грустно. Надо, помозговать, возможно, придумается какой-нибудь компромисс.
имхо, опасно это. от порядка подключения хидеров зависит.
Здравствуйте, night beast, Вы писали:
R>>Хотя, серьезным недостатком это подхода является невозможность модификации счетчиков внутри классов. Да и при использовании в разных пространствах имен тоже все грустно. Надо, помозговать, возможно, придумается какой-нибудь компромисс.
NB>имхо, опасно это. от порядка подключения хидеров зависит.
Пока я не совсем понимаю, в чем опасность. Да и зависимости от порядка подключения тоже пока не вижу. Значение __LINE__ от порядка подключения не зависит, так что с ODR все в продяке, вроде бы. Так где же грабли?
[Upd]
Возможно, ты имеешь в виду использование одних и тех же счетчиков в разных заголовочных файлах? Да, с этим, конечно же проблема. Нот э таргет сценарио, как говорится
И да, это весомый аргумет в пользу явного создания счетчиков — компилятор убережет от случайных ошибок.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, rg45, Вы писали:
R>[Upd] R>Возможно, ты имеешь в виду использование одних и тех же счетчиков в разных заголовочных файлах? Да, с этим, конечно же проблема. Нот э таргет сценарио, как говорится
да. речь об этом.
если счетчик используется только во одном заголовочном файле, то нужен ли он вообще?
Здравствуйте, rg45, Вы писали:
R>Между делом придумался вот компайл-тайм счетчик. При всей простоте допускает одновременное использование нескольких независимых счетчиков. Также позволяет настраивать точку отчсета и шаг.
1. Засоряет пространство типов — шаблоны воплощаются для каждого номера строки. Лучше попробовать пересадить на __COUNTER__.
2. Если __LINE__ проваливается в рантайм (микрософтовский режим сборки "edit&continue") — будут проблемы. См. выше
А вообще, прикольный способ, по сравнению с более старым трюком на контекстно-зависимом выборе наилучшей специализации.
(Сейчас на память не воспроизведу, как там это было устроено).
Здравствуйте, night beast, Вы писали:
R>>[Upd] R>>Возможно, ты имеешь в виду использование одних и тех же счетчиков в разных заголовочных файлах? Да, с этим, конечно же проблема. Нот э таргет сценарио, как говорится
NB>да. речь об этом. NB>если счетчик используется только во одном заголовочном файле, то нужен ли он вообще?
Ну это уже зависит от индивидуальных потребностей, лично мне эти счетчики имено для этого и нужны. Я их использую для декларативного описания больших структур данных (XML, например, и др.) как счетчики классов и полей. При этом счетчки, в большинстве своем, создаются в области видимости класса. А "размазывать" класс по нескольким заголовкам у меня нет необходимости
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, Кодт, Вы писали:
К>1. Засоряет пространство типов — шаблоны воплощаются для каждого номера строки. Лучше попробовать пересадить на __COUNTER__.
С использованием __COUNTER__ — это мой текущий подход. Он обладает серьезнейшим ограничением — не позволяет иметь одновременно несколько независимых счетчиков. Это невероятно усложняет средства декларативного описания иерархических структур.
[Upd]
Кроме того, __COUNTER__, в отличие от __LINE__, может давать разнные последовательности чисел при включении в разные единицы трансляции и поэтому требует дополнительных телодвижений для соблюдения ODR.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, Кодт, Вы писали:
К>А вообще, прикольный способ, по сравнению с более старым трюком на контекстно-зависимом выборе наилучшей специализации. К>(Сейчас на память не воспроизведу, как там это было устроено).
Здравствуйте, rg45, Вы писали:
R>Пока я не совсем понимаю, в чем опасность. Да и зависимости от порядка подключения тоже пока не вижу. Значение __LINE__ от порядка подключения не зависит, так что с ODR все в продяке, вроде бы. Так где же грабли?
R>[Upd] R>Возможно, ты имеешь в виду использование одних и тех же счетчиков в разных заголовочных файлах? Да, с этим, конечно же проблема. Нот э таргет сценарио, как говорится R>И да, это весомый аргумет в пользу явного создания счетчиков — компилятор убережет от случайных ошибок.
Проблема в том, что с помощью include можно нарушать монотонность значений __LINE__ в рамках одной единицы трансляции.
// setup.h
CREATE_CTC(foo, 1000)
// some.hconst int x = GET_CTC(foo); // __LINE__ = 2 --> 1000
UPDATE_CTC(foo) // __LINE__ = 3 --> 1001const int y = GET_CTC(foo); // __LINE__ = 4 --> 1001
// some.cc - хороший#include"setup.h"#include"some.h"
.....
UPDATE_CTC(foo) // __LINE__ = 101 --> 1002const int z = GET_CTC(foo); // __LINE__ = 102 --> 1002const int t = GET_CTC(foo); // __LINE__ = 103 --> 1002
// some.cc - плохой#include"setup.h"
.....
UPDATE_CTC(foo) // __LINE__ = 100 --> 1001const 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) // ошибка компиляции - специализация после использования
Здравствуйте, rg45, Вы писали:
К>>1. Засоряет пространство типов — шаблоны воплощаются для каждого номера строки. Лучше попробовать пересадить на __COUNTER__.
R>С использованием __COUNTER__ — это мой текущий подход. Он обладает серьезнейшим ограничением — не позволяет иметь одновременно несколько независимых счетчиков. Это невероятно усложняет средства декларативного описания иерархических структур. R>Кроме того, __COUNTER__, в отличие от __LINE__, может давать разнные последовательности чисел при включении в разные единицы трансляции и поэтому требует дополнительных телодвижений для соблюдения ODR.
Я имел в виду, что ты свои CTC перепишешь, используя внутри __COUNTER__ вместо __LINE__.
Для __COUNTER__ гарантируется монотонность и уникальность в пределах одной единицы трансляции, невзирая на старания препроцессора по склеиванию из инклудов и макросов.
А чтобы соблюсти ODR, — для этого же есть анонимные пространства имён?
Просто заверни определения своих шаблонов в namespace{....} — и всё.
Здравствуйте, Кодт, Вы писали:
К>Я имел в виду, что ты свои CTC перепишешь, используя внутри __COUNTER__ вместо __LINE__. К>Для __COUNTER__ гарантируется монотонность и уникальность в пределах одной единицы трансляции, невзирая на старания препроцессора по склеиванию из инклудов и макросов. К>А чтобы соблюсти ODR, — для этого же есть анонимные пространства имён? К>Просто заверни определения своих шаблонов в namespace{....} — и всё.
Спасибо за дельные советы. Переделал с учетом замечаний. Для соблюдения ODR, вместо анонимных пространств имен, использовал несколько другой подход, который позволяет использовать счетчики в области видимости классов. Просто в момент создания счетчика защщелкиваем значение __COUNTER__ в константу (counter_base) и отнимаем ее при всех последуюющих обращениях к __COUNTER__.
Здравствуйте, rg45, Вы писали:
R>Спасибо за дельные советы. Переделал с учетом замечаний. Для соблюдения ODR, вместо анонимных пространств имен, использовал несколько другой подход, который позволяет использовать счетчики в области видимости классов. Просто в момент создания счетчика защщелкиваем значение __COUNTER__ в константу (counter_base) и отнимаем ее при всех последуюющих обращениях к __COUNTER__.
Вот это и будет нарушением одр.
Защёлкивать __COUNTER__ можно только в местную константу, ибо!
Здравствуйте, Кодт, Вы писали:
R>>Спасибо за дельные советы. Переделал с учетом замечаний. Для соблюдения ODR, вместо анонимных пространств имен, использовал несколько другой подход, который позволяет использовать счетчики в области видимости классов. Просто в момент создания счетчика защщелкиваем значение __COUNTER__ в константу (counter_base) и отнимаем ее при всех последуюющих обращениях к __COUNTER__.
К>Вот это и будет нарушением одр. К>Защёлкивать __COUNTER__ можно только в местную константу, ибо!
Не будет! Ибо константы и так имеют внутреннее связывание по умолчанию. Правда, это не касается констант-членов класса Но это не так уж сложно разрулить. Уже в процессе...
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, Goodhope, Вы писали:
G>Здравствуйте, rg45, Вы писали:
G>Прошу прощения за глупый вопрос — а зачем это может быть нужно?
В двух словах ответить даже как-то затрудняюсь, думаю применений можно придумать множество самых разных. Мне эти счетчики помогают заставить компилятор писать программы вместо меня
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, rg45, Вы писали:
К>>Вот это и будет нарушением одр. К>>Защёлкивать __COUNTER__ можно только в местную константу, ибо!
R>Не будет! Ибо константы и так имеют внутреннее связывание по умолчанию. Правда, это не касается констант-членов класса Но это не так уж сложно разрулить. Уже в процессе...
Ну так именно это я и сделал: вытащил защёлку из членов в свободные.
Здравствуйте, Goodhope, Вы писали:
G>Прошу прощения за глупый вопрос — а зачем это может быть нужно?
Ну обычно для формирования всяких табличных монотонных имен и значений по типу енума, но с произвольным шагом.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Здравствуйте, Кодт, Вы писали:
К>Ну так именно это я и сделал: вытащил защёлку из членов в свободные.
Идея, конечно, понятна но пользоваться этим не очень удобно. Создавая счетчик в скопе класса, нужно не забыть определить для него вспомогательную сущность в пространсте имен. Простота и элеганость сразу же страдают, а следовательно и надежность тоже. А между тем, нам ведь не нужно заводить базу для каждого счетчика в отдельности — достаточно одной на заголовочный файл. Меня __LINE__ потому и привлекла, что именно этой проблемы она не создавала (а проблем перечисленных выше просто не возникало при моих сценариях использования). Как водится, приходим к тому, что идеального варианта не существует, чем-то нужно пожертвовать?
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, rg45, Вы писали:
R>Между делом придумался вот компайл-тайм счетчик. При всей простоте допускает одновременное использование нескольких независимых счетчиков. Также позволяет настраивать точку отчсета и шаг.
Я сделал в свое время похожую штуку — и напоролся на ограничение рекурсии инстанцирования для сколько-нибудь больших файлов. Но это было давно, может, сейчас это и не проблема уже...