Предисловие
Представьте, что у меня есть шаблон: template<class... Opts> class rqueue
, в котором могут быть различные функции, выбранные с помощью тегов (специальные структуры опций), переданные в список параметров, например.
rqueue<trace_record, opt::fixed_size<>> trace;
rqueue<trace_record::flat, opt::fixed_size<>, opt::virtual_offset<>> info;
Первая версия (trace
) представляет собой очередь записей, которая будет использоваться для записи записей трассировки (opt::fixed_size
ограничивает ее размер до 4096 байт). Вторая версия (info
) будет заполнена из первой (каким-то потоком, который перепишет записи с преобразованием в плоское представление), но важно то, что opt::virtual_offset<>
добавляет эти методы:
off_t start(); // virtual offset of oldest record
off_t stop(); // virtual offset of future record (when next gets written)
и различные другие функции (offset_to_iterator()
), основанные на этом виртуальном смещении, которое всегда растет (с каждой записью), что имитирует виртуальную память размером, например. 4 ГБ (когда в качестве смещения используется unsigned
, оно может быть еще больше при size_t
или unisgned long long
), где фактический буфер (размером, например, 4096 Б) создает окно внутри этой виртуальной памяти.
Ссылка на мой другой связанный вопрос - помощник пакета параметров который был специально разработан для этого шаблона.
(Обратите внимание, что существует множество других функций, которые можно комбинировать независимо друг от друга, например opt::rqueue_listener
, которые можно использовать для сообщения о различных событиях).
Проблема
Мне удалось создать этот шаблон со всеми возможными функциями, где некоторые методы являются фиктивными, когда функция не выбрана (например, start()
возвращает ноль, а stop()
в этом случае совпадает с size()
), но я хотел бы как-то скрыть методы, если функция не выбрана. Есть идеи?
(Еще одним примером может быть фиктивный set_listener(void*)
, если opt::rqueue_listener
не было включено — этот параметр можно комбинировать с любым другим параметром.)
EDIT: Представьте себе, например. using off_t = conditional_t<something,the_type,void>
и private: off_t start_()
. Я хочу:
- Пусть
public: off_t start()
позвонит этомуstart_()
, еслиoff_t
неvoid
- Не иметь метода
start()
, еслиoff_t
недействителен (или выполнено какое-то условие). В качестве альтернативы какой-тоstatic_assert
, если я попытаюсь его вызвать.
Мои попытки
Я думал о слиянии класса с расширителями, которые бы публиковали функции, приводя себя (*this
) к реальному классу (rqueue<...>&
) и перенаправляя вызовы туда (в приватные методы, где расширитель становится другом). Я создал еще один помощник template<class... Bases> class merge
, который может наследовать любой выбранный класс, игнорируя любой переданный void
. Это сработало, но решение довольно уродливое, мне это не нравится.
Другое возможное решение, которое пришло мне в голову, состояло в том, чтобы создать некоторую базовую реализацию (в качестве другого шаблона, возможно, скрытого в каком-то namespace detail
) и использовать цепочку специализаций шаблонов, которые публиковали бы методы на основе параметров. Проблема в том, что количество комбинаций быстро растет и может возникнуть еще одна проблема с friend
-ингом класса для доступа к приватным методам записи (первый параметр передается в шаблон).
Мои попытки SFINAE и static_assert
часто заканчивались ошибками компилятора, жалующимися на то, что специализация методов не разрешена в шаблонах (или частичная специализация) или static_assert срабатывает, когда этого не должно быть. Я ожидаю, что есть какое-то хорошее решение. Жду с нетерпением :)
fill_empty
принимает два параметра - тип и значение:template <class T, T V> struct fill_empty: tag::fill_empty { typedef T type; static constexpr T value = V; };
- person firda   schedule 13.10.2014start()
функцию-член, но вы хотите, чтобы она вызывалаvirtual_offset<>::start()
, когда ваш класс наследуется отvirtual_offset<>
, иначе она была бы недоступна? - person Piotr Skotnicki   schedule 13.10.2014public: off_t start()
, еслиopt::virtual_offset<...>
не было включено (off_t
в этом случае можно получить что угодно, мойopt::bind
поможет определить егоvoid
, если вам нужно). - person firda   schedule 13.10.2014virtual
, этоtemplate
спроектировано так, чтобы быть оченьlight-weight
, если не включена специальная функция (например,rqueue<trace_record>
будет базовой, без дополнительных данных и методов). - person firda   schedule 13.10.2014static_assert(!std::is_same<off_t, void>::value, "!");
в общедоступнойstart()
функции-члене нельзя? - person Piotr Skotnicki   schedule 13.10.2014static_assert
. Должно быть, я слишком много работал в прошлый раз, чтобы пропустить это простое решение :D - person firda   schedule 13.10.2014