Статический, если на простом С++?

Проблема:
Как реализовать функцию static if, предложенную в С++ 11, в простой С++?

История и оригинальная проблема:
Недавно я столкнулся с такой проблемой. Мне нужен класс Sender с интерфейсом вроде

class Sender
{
   void sendMessage( ... );
   void sendRequest( ... );
   void sendFile( ... );
   // lots of different send methods, not important actually
}

В некоторых случаях мне нужно будет создать DoubleSender, то есть экземпляр этого класса, который дважды будет вызывать его методы, то есть при вызове, скажем, в методе sendMessage(...) одно и то же сообщение нужно отправить дважды.

Мои решения:
Первый подход:
Имейте член isDouble, и в конце каждого вызова метода выполните проверку

sendMessage(...) { ... if( isDouble ) { sendMessage( ... ); }

Ну, я не хочу этого, потому что на самом деле мне понадобится двойная публикация совсем недавно, и эта часть кода в критическом для времени разделе будет пассивным на 98%.

Второй подход:
Наследуйте класс DoubleSender от Sender и реализуйте его методы, например:

void DoubleSender::sendMessage( ... )
{
   Sender::sendMessage(...);
   Sender::sendMessage(...);
}

Ну, это приемлемо, но занимает много места неприятного кода (очень много, потому что существует множество разных методов send...

Третий подход:
Представьте, что я использую С++ 11:). Затем я могу сделать этот класс обобщенным и создать необходимую часть кода в соответствии с аргументом tempalte, используя static if:

enum SenderType { Single, Double };
template<SenderType T>
class Sender
{
   void sendMessage(...)
   {
      // do stuff
      static if ( T == Single )
      {
         sendMessage(...);
      }
   }
};

Это короче, легче читать, чем предыдущие решения, не генерирует дополнительный код и... это С++ 11, который, к сожалению, я не могу использовать в своей работе.

Итак, вот где я пришел к моему вопросу - как я могу реализовать аналог static if в С++?
Кроме того, я был бы признателен за любые другие предложения о том, как решить мою оригинальную проблему.
Спасибо заранее.

Ответы

Ответ 1

Цитата @JohannesSchaubLitb

с моим static_if, который работает на gcc, можно сделать это:)
в ограниченном количестве

(см. также здесь)

Этот трюк включает специфическую интерпретацию GCC спецификаций на Lambdas в С++ 11. Таким образом, он (скорее всего) станет отчет о дефектах против стандарта. Это приведет к тому, что трюк больше не работает в более поздней версии GCC (он уже не работает в версии 4.7).

См. комментарий ниже для получения более подробной информации от Johanness

http://ideone.com/KytVv:

#include <iostream>

namespace detail {
template<bool C>
struct call_if { template<typename F> void operator<<(F) { } };

template<>
struct call_if<true> {
  template<typename F>
  void operator<<(F f) { f(); }
};
}

#define static_if(cond) detail::call_if<cond>() << [&]

template<bool C, typename T>
void f(T t) {
  static_if(C) {
    t.foo();
  };
}

int main() {
  f<false>(42);
}

Ответ 2

Почему бы не сделать реализацию отправки политикой класса отправителя и использовать CRTP:

template<class Derived>
class SingleSenderPolicy
{
    public:
    template< class memFunc >
    void callWrapperImpl(memFunc f, ...)
    {
        static_cast<Derived *>(this)->f(...);
    }
};

template< class Derived >
class DoubleSenderPolicy
{
    public:
    template< class memFunc >
    void callWrapperImpl(memFunc f, ...)
    {
        static_cast<Derived *>(this)->f(...);
        static_cast<Derived *>(this)->f(...);
     }
};

template< class SendPolicy>
class Sender : public SendPolicy< Sender >
{
public:
    void sendMessage( ... )
    {
       // call the policy to do the sending, passing in a member function that
       // acutally performs the action
       callWrapperImpl( &Sender::sendMessageImpl, ... );
    }

    void doSomethingElse( ... )
    {
       callWrapperImpl( &Sender::doSomethingElseImpl, ... );
    }


protected:
    void sendMessageImpl(... )
    {
        // Do the sending here
    } 

    void doSomethingElseImpl(... )
    {
        // Do the sending here
    } 
};

Общественный sendXXX функционирует в вашем классе, просто пересылает оболочку вызова, передавая в функцию-член, которая реализует реальную функциональность. Эта функция-член будет вызываться в соответствии с SendPolicy класса. CRTP сохраняет использование привязки для переноса аргументов и этого указателя с помощью функции-члена для вызова.

С помощью одной функции он действительно не сокращает количество кода, но если у вас много вызовов, это может помочь.

Примечание. Этот код является скелетом, чтобы обеспечить возможное решение, оно не было скомпилировано.

Примечание: Sender<DoubleSenderPolicy> и Sender<SingleSenderPolicy> являются совершенно разными типами и не разделяют динамические отношения наследования.

Ответ 3

Большинство компиляторов делают постоянную фальцовку и удаление мертвого кода, поэтому, если вы пишете регулярный оператор if:

enum SenderType { Single, Double };
template<SenderType T>
class Sender
{
   void sendMessage(...)
   {
      // do stuff
      if ( T == Single )
      {
         sendMessage(...);
      }
   }
};

Отключенная ветка if будет удалена при генерации кода.

Необходимость static if заключается в том, что инструкции будут вызывать ошибку компилятора. Поэтому скажите, что у вас есть что-то вроде этого (его несколько psuedo-код):

static if (it == random_access_iterator)
{
    it += n;
}

Поскольку вы не можете вызвать += для итераторов неслучайного доступа, тогда код всегда будет не скомпилирован с помощью регулярного оператора if, даже с удалением мертвого кода. Поскольку компилятор по-прежнему будет проверять синтаксис перед удалением кода. При использовании static if компилятор пропустит проверку синтаксиса, если условие не соответствует действительности.

Ответ 4

std::string a("hello world");
// bool a = true;
if(std::is_same<std::string, decltype(a)>::value) {
    std::string &la = *(std::string*)&a;
    std::cout << "std::string " << la.c_str() << std::endl;
} else {
    bool &la = *(bool*)&a;
    std::cout << "other type" << std::endl;
}