Статический, если на простом С++?
Проблема:
Как реализовать функцию 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;
}