Отправка на основе типа компиляции

Следуя методам "Modern С++ Design", я реализую библиотеку персистентности с различными оптимизациями во время компиляции. Я хотел бы, чтобы возможность отправлять функцию в шаблонную переменную-член, если эта переменная получена из данного класса:

template<class T, template <class> class Manager = DefaultManager> class Data
{
private:
   T *data_;

public:
   void Dispatch()
   {
      if(SUPERSUBCLASS(Container, T))
      {
         data_->IKnowThisIsHere();
      }
      else
      {
         Manager<T>::SomeGenericFunction(data_);
      }
   }
}

Где SUPERSUBCLASS - это макрос времени компиляции для определения наследования объектов. Конечно, это не удается во всех случаях, когда T наследует от Container (или T является внутренним типом и т.д.), Потому что компилятор справедливо жалуется, что IKnowThisIsHere() не является членом данных, хотя этот путь кода никогда не будет соблюден, как показано здесь после предварительной обработки с помощью T = int:

private:
   int *data_;

public:
   void Dispatch()
   {
      if(false)
      {
         data_->IKnowThisIsHere();

Компилятор явно жалуется на этот код, хотя он никогда не будет выполнен. Предложение об использовании dynamic_cast также не работает, поскольку повторное преобразование типа выполняется во время компиляции, что невозможно (например, с T = double, std::string):

void Dispatch()
   {
      if(false)
      {
         dynamic_cast<Container*>(data_)->IKnowThisIsHere();

error: cannot dynamic_cast '((const Data<double, DefaultManager>*)this)->Data<double, DefaultManager>::data_' (of type 'double* const') to type 'class Container*' (source is not a pointer to class)
error: cannot dynamic_cast '((const Data<std::string, DefaultManager>*)this)->Da<sttad::string, DefaultManager>::data_' (of type 'struct std::string* const') to type 'class Container*' (source type is not polymorphic)

Мне действительно нужно подражать (или действительно убеждать!), когда компилятор испускает один набор кода, если T наследует от Container, а другой, если он этого не делает.

Любые предложения?

Ответы

Ответ 1

Перегрузка может быть полезна для реализации диспетчеризации времени компиляции, как предложено Александреску в его книге "Современный дизайн С++".

Вы можете использовать такой класс, чтобы преобразовать во время компиляции логическое или целое число в тип:

template <bool n>
struct int2type
{ enum { value = n}; };

Следующий исходный код показывает возможное приложение:

#include <iostream>

#define MACRO()   true  // <- macro used to dispatch 

template <bool n>
struct int2type
{ enum { value = n }; };

void method(int2type<false>)
{ std::cout << __PRETTY_FUNCTION__  << std::endl; }

void method(int2type<true>)
{ std::cout << __PRETTY_FUNCTION__  << std::endl; }

int
main(int argc, char *argv[])
{
    // MACRO() determines which function to call
    //

    method( int2type<MACRO()>()); 

    return 0;
}

Конечно, что действительно делает работу MACRO() или лучшей реализацией в качестве метафлора

Ответ 2

Вам требуется время компиляции if. Затем это вызывает функцию, зависящую от того, какой случай true. Таким образом, компилятор не наткнется на код, который он не может скомпилировать (потому что он безопасно хранится в другом шаблоне функции, который никогда не создается экземпляром).

Существует несколько способов реализации такого времени компиляции if. Наиболее распространенным является использование идиомы SFINAE: сбой замены не является ошибкой. Boost is_base_of ist фактически является экземпляром этой идиомы. Чтобы использовать его правильно, вы не будете писать его в выражении if, а скорее используйте его как возвращаемый тип своей функции.

Неподтвержденный код:

void Dispatch()
{
    myfunc(data_);
}

private:

// EDIT: disabled the default case where the specialisation matched
template <typename U>
typename enable_if_c<is_base_of<Container, U>::value, U>::type myfunc(U& data_) {
    data_->IKnowThisIsHere();
}

template <typename U>
typename disable_if_c<is_base_of<Container, U>::value, U>::type myfunc(U& data_) { // default case
    Manager<U>::SomeGenericFunction(data_);
}

Ответ 3

У более высоких признаков есть что-то для этого: is_base_of

Ответ 4

Посмотрите на библиотеку метапрограммного расширения boost. Кроме того, в зависимости от того, что вы пытаетесь выполнить, посмотрите на библиотеку сериализации boost, поскольку она может уже иметь то, что вам нужно.

Ответ 5

Я заинтересован в том, чтобы делать это "с первых принципов" как образовательное любопытство. Тем не менее, я посмотрю на библиотеки Boost.

В любом случае, я не думаю, что is_base_of - любая помощь - он делает точно так же, как макрос SUPERSUBCLASS...

Ответ 6

К сожалению, я тоже прошел через это (и это тоже вызов во время выполнения;)). Компилятор жалуется, что если вы передаете не полиморфные или типы классов, аналогично предыдущему:

error: cannot dynamic_cast '((const Data<double, DefaultManager>*)this)->Data<double, RawManager>::data_' (of type 'double* const') to type 'class Container*' (source is not a pointer to class)

или

error: cannot dynamic_cast '((const Data<std::string, DefaultRawManager>*)this)->Data<std::string, DefaultManager>::data_' (of type 'struct std::string* const') to type 'class Container*' (source type is not polymorphic)