Отправка на основе типа времени компиляции - PullRequest
4 голосов
/ 28 сентября 2008

Следуя методам 'Modern C ++ 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, и другой, если это не так.

Есть предложения?

Ответы [ 6 ]

3 голосов
/ 28 сентября 2008

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

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

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 голосов
/ 28 сентября 2008

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

Существует несколько способов реализации такого времени компиляции if. Наиболее распространенным является использование идиомы SFINAE: ошибка замещения не является ошибкой . Boost's is_base_of фактически является экземпляром этой идиомы. Чтобы использовать его правильно, вы бы не написали это в выражении 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_);
}
1 голос
/ 28 сентября 2008

В чертах усиления есть что-то для этого: is_base_of

0 голосов
/ 28 сентября 2008

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

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)
0 голосов
/ 28 сентября 2008

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

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

0 голосов
/ 28 сентября 2008

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...