Question

I am testing combinations of various optimizations and for these I need a static-if as described in http://channel9.msdn.com/Events/GoingNative/GoingNative-2012/Static-If-I-Had-a-Hammer to enable and disable specific optimizations. if(const-expr) does not always work as some optimizations involve changing the data layout and this can not be done at function scope.

Basically what I want is this:

template<bool enable_optimization>
class Algo{
  struct Foo{
    int a;
    if(enable_optimization){
      int b;
    }

    void bar(){
      if(enable_optimization){
        b = 0;
      }
    }
  };
};

(Yes, the smaller memory footprint of removing b from the data layout is relevant in my case.)

Currently I am faking it using a very bad hack. I am looking for a better way of doing this.

File a.h

#ifndef A_H
#define A_H
template<class enable_optimization>
class Algo;
#include "b.h"
#endif

File b.h (this file is autogenerated from a Python script)

#define ENABLE_OPTIMIZATION 0
#include "c.h"
#undef
#define ENABLE_OPTIMIZATION 1
#include "c.h"
#undef

File c.h

template<>
class Algo<ENABLE_OPTIMIZATION>{
  struct Foo{
    int a;
    #if ENABLE_OPTIMIZATION
    int b;
    #endif

    void bar(){
      #if ENABLE_OPTIMIZATION
      b = 0;
      #endif
    }
  };
};

Does anyone know of a better way of doing this? Theoretically it can be done using template meta programming and at first I used it. At least the way I used it was a pain in the ass and lead to completely unreadable and bloated code. Using the hack above resulted in a significant productivity boost.

EDIT: I have several optimization flags and these interact.

No correct solution

OTHER TIPS

There's no reason the code should get much more complicated using templates:

template<bool enable_optimization>
  class FooOptimized
  {
  protected:
    int b;
    void bar_optim()
    {
      b = 0;
    }
  };

template<>
  class FooOptimized<false>
  {
  protected:
    void bar_optim() { }
  };

template<bool enable_optimization>
  struct Algo
  {
    struct Foo : FooOptimized<enable_optimization>
    {
      int a;

      void bar()
      {
        this->bar_optim();
      }
    };
  };

No metaprogramming needed, just separate the parts that vary based on whether optimisation is enabled into a new type and specialize it.

Because the new type is used as a base class when it is empty (i.e. there is no FooOptimized::b member) it will take up no space, so sizeof(Algo<false>::Foo) == sizeof(int).


(Feel free to ignore the rest of this answer, it doesn't address the question directly, instead it suggest a different approach that has different trade-offs. Whether it is "better" or not depends entirely on the details of the real code, which are not shown in the trivial example given in the question.)

As a related, but separate issue, the parts of Algo and Algo::Foo that don't depend on whether optimization is enabled are still dependent on the template parameter, so although you only write those bits of code once, the compiler will generate two sets of object code. Depending how much work is in that code and how it's used you might find there is an advantage to changing that into non-template code, i.e. replacing static polymorphism with dynamic polymorphism. For example, you could make the enable_optimization flag a runtime constructor argument instead of template argument:

struct FooImpl
{
  virtual void bar() { }
};

class FooOptimized : FooImpl
{
  int b;
  void bar()
  {
    b = 0;
  }
};

struct Algo
{
  class Foo
  {
    std::unique_ptr<FooImpl> impl;
  public:
    explicit
    Foo(bool optimize)
    : impl(optimize ? new FooOptimized : new FooImpl)
    { }

    int a;

    void bar()
    {
      impl->bar();
    }
  };
};

You'd have to profile and test it to determine whether the virtual function has more of less overhead than the duplication of code in Algo and Algo::Foo that doesn't depend on the template parameter.

Note: as it stands, this approach doesn't work, as there appears to be no way to have a member in a class without allocating space for that member. If you have an idea how to make it work, feel free to edit.

You could use an idiom like this:

template<bool optimized, typename T> struct dbg_obj {
  struct type {
    // dummy operations so your code will compile using this type instead of T
    template<typename U> type& operator=(const U&) { return *this; }
    operator T&() { return *static_cast<T*>(0); /* this should never be executed! */ }
  };
};
template<typename T> struct dbg_obj<false, T> {
  typedef T type;
};

template<bool enable_optimization>
class Algo{
  struct Foo{
    int a;
    typename dbg_obj<enable_optimization, int>::type b;

    void bar(){
      if(enable_optimization){
        b = 0;
      }
    }
  };
};

If optimization is disabled, this gives you a normal int member. If optimization is enabled, then the type of b is a struct without members, which will not take any space.

As your method bar uses what looks like a runtime if to decide whether or not to access b, as opposed to a clear compile-time mechanism like template specialization, all the operations you use on b must be available from the dummy struct as well. Even if the relevant sections will never get executed, and the compiler most likely optimizes them away, the correctness checks come first. So the line b = 0 must compile for the replacement type as well. That's the reason for the dummy assignment and the dummy cast operations. Although either would suffice for your code, I included both in case they prove useful at some other point, and to give you an idea how to add more should you ever require them.

The static solution presented by Jonathan Wakely (not the dynamic one) is the way to go.

The idea is simple:

  1. Isolate the "specific" data into its own class (templated and specialized accordingly)
  2. All accesses to that data is completely handed over to the specialized class (the interface must be uniform)

Then, in order not to incur space overhead, you use the EBO (Empty Base Optimization) at your advantage by inheriting either specialization of this special class.

Note: an attribute is required to take at least 1 byte of space, a base class is not in some conditions (such as being empty).

In your case:

  • b is the specific data
  • there is a single operation involving it (which consist in setting its value)

So we can easily construct a class:

template <typename T> struct FooDependent;

template <>
struct FooDependent<true> {
    int b;
    void set(int x) { b = x; }
};

template <>
struct FooDependent<false> {
    void set(int) {}
};

And then we can inject it into the Foo, using EBO at our advantage:

struct Foo: FooDependent<enable_optimization> {
    int a;

    void bar() { this->set(0); }
};

Note: use this in templated code to access members of base classes, or good compilers will reject your code.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top