13

Problem in short:
How could one implement static if functionality, proposed in c++11, in plain c++ ?

History and original problem:
Recently I came up with a problem like this. I need a class Sender with an interface like

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

In some cases I will need to create a DoubleSender, i.e. an instance of this class, which would call its methods twice, i.e. when calling, let's say, a sendMessage(...) method, the same message has to be sent twice.

My solutions:
First approach:
Have an isDouble member, and in the end of each method call make a check

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

Well, I don't want this, because actually I will need double posting very recently, and this part of code in time-critical section will be 98% passive.

Second approach:
Inherit a class DoubleSender from Sender, and implement its methods like:

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

Well, this is acceptable, but takes much space of unpleasant code (really much, because there are lots of different send.. methods.

Third approach:
Imagine that I am using c++11 :). Then I can make this class generic and produce the necessary part of code according to tempalte argument using static if:

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

This is shorter, easier to read than previous solutions, does not generate additional code and... it's c++11, which I unfortunately cannot use in my work.

So, here is where I came to my question - how can I implement static if analog in c++ ?
Also, I would appreciate any other suggestions about how to solve my original problem.
Thanks in advance.

Mechanical snail
  • 29,755
  • 14
  • 88
  • 113
Grigor Gevorgyan
  • 6,753
  • 4
  • 35
  • 64
  • 1
    You could look at how Boost does it with its [static_assert](http://www.boost.org/doc/libs/1_49_0/doc/html/boost_staticassert.html) maybe? – Some programmer dude Mar 30 '12 at 10:18
  • You may want to familiarize yourself with boost.mpl – Anycorn Mar 30 '12 at 10:24
  • 10
    `static if` doesn't exist in C++11. – Nicol Bolas Mar 30 '12 at 10:42
  • 1
    As stated `static if` does not exist yet, it is just an idea by Andrei Alexandescu (see: http://channel9.msdn.com/Events/GoingNative/GoingNative-2012/Static-If-I-Had-a-Hammer). In the meantime you could used `std::enable_if` (c++11) or the `boost` equivalent – mark Mar 30 '12 at 11:12
  • I'm actually not convinced the last version is easier to read than the second. – Mark B Mar 30 '12 at 12:30
  • static if have been proposed for the next standard, not c++11. – Klaim Jul 25 '12 at 21:20
  • Note that `if constexpr` (formerly known as `static if`) may now very well be part of C++17. – P-Gn May 20 '16 at 12:19

4 Answers4

9

Quoting @JohannesSchaubLitb

with my static_if that works on gcc one can do it :)
in some limited fashion

(see also here)

This trick involves a specific GCC interpretation of the specs on Lambdas in C++11. As such, it will (likely) become a defect report against the standard. This will lead to the trick no longer working in more recent version of GCC (it already doesn't work in 4.7).

See the comment thread below for some more details from 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);
}
Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633
  • That thing only works on GCC 4.5.1 because of a bug in that the content of the lambda isn't properly checked. You should get a "type 'int' has no member named 'foo'" error. – Xeo Mar 30 '12 at 15:28
  • 1
    @Xeo can you be more precise? It's not a bug but an interpretation of what the Standard does not define very precisely (instantiation of members of local classes will not be done unless they are required by GCC and Clang). Comeau/EDG interpret it differently and always instantiate all members of the local class. We will have this as a new issue report in the next issue list. – Johannes Schaub - litb Mar 30 '12 at 20:18
  • Unfortunately GCC4.7 always appearss to instantiate operator() (for whatever reason), so this trick does not work anymore with them. Neither for Clang :( – Johannes Schaub - litb Mar 30 '12 at 20:27
  • With some boost.PP you could emulate this by creating a custom local class and have syntax like `static_if((t), C) { t.foo(); }`. Silly of them to deviate from their existing practice of not instantiating member functions unless they are used, IMO. – Johannes Schaub - litb Mar 30 '12 at 20:50
  • Thanks @JohannesSchaub-litb for the additional information and for letting me borrow your trick :) – sehe Mar 30 '12 at 22:36
  • 1
    I believe GCC 4.7 and higher now supports `__builtin_choose_expr` in C++ code, which can be used to create a form of static if. – Paul Fultz II May 08 '12 at 23:14
7

Why not make the send implementation a policy of the sender class and use 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
    } 
};

The public sendXXX functions in you class simply forward to the call wrapper, passing in a member function that implements the real functionality. This member function will be called according to the SendPolicy of the class. CRTP saves the use of bind to wrap the arguments and this pointer up with the member function to call.

With one function it doesn't really cut down on the amount of code, but if you have a lot of calls it could help.

Note: This code is a skeleton to provide a possible solution, it has not been compiled.

Note: Sender<DoubleSenderPolicy> and Sender<SingleSenderPolicy> are completely different types and do not share a dynamic inheritance relationship.

mark
  • 7,381
  • 5
  • 36
  • 61
5

Most compilers do constant folding and dead code removal, so if you write a regular if statement like this:

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

The if branch will get removed when the code is generated.

The need for static if is when the statements would cause a compiler error. So say you had something like this(its somewhat psuedo code):

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

Since you can't call += on non-random access iterators, then the code would always fail to compile with a regular if statement, even with dead code removal. Because the compiler still will check the syntax for before removing the code. When using static if the compiler will skip checking the syntax if the condition is not true.

Paul Fultz II
  • 17,682
  • 13
  • 62
  • 59
0
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;
}
Igor Milyakov
  • 602
  • 5
  • 7