Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ template metaprogramming, "static if" workaround - can it be improved?

I have a function that scans the user's file system, fills a vector with the paths, then either sorts it or not. Since the user should be able to decide at compile-time whether he wants the vector sorted or not, I use templates and helper classes in place of a much desired (but not existing) "static if".

Consider this code:

enum class Sort{Alphabetic, Unsorted};  

template<Sort TS> struct SortHelper;
template<> struct SortHelper<Sort::Alphabetic>
{
    static void sort(vector<string>& mTarget) { sort(begin(mTarget), end(mTarget)); }
};
template<> struct SortHelper<Sort::Unsorted>
{
    static void sort(vector<string>&) { }
};

template<Sort TS> struct DoSomethingHelper
{
    static void(vector<string>& mTarget)
    {
         // do something with mTarget
         SortHelper<TS>::sort(mTarget);
    }   
};

The code I've written above is GREATLY simplified from the original, which takes multiple template parameters to allow the user to customize even further the results of the function at compile-time.

Is there an alternative to using all of these helper classes? It gets really messy and hard to read.

Ideally, this is what I would like to write:

enum class Sort{Alphabetic, Unsorted};  
template<Sort TS> struct DoSomethingHelper
{
    static void(vector<string>& mTarget)
    {
         // do something with mTarget
         static_if(TS == Sort::Unsorted) { /* do nothing */ }
         static_if(TS == Sort::Alphabetic) { sort(begin(mTarget), end(mTarget)); }
    }   
};
like image 826
Vittorio Romeo Avatar asked Jun 10 '13 13:06

Vittorio Romeo


2 Answers

Since your value is known at compile time (non-template type parameter) you can perfectly write a "normal" if:

template<Sort TS>
void someFunction(vector<string>& mTarget)
{
     if (TS == Sort::Alphabetic) { sort(begin(mTarget), end(mTarget)); }
     // else if (TS == Sort::Unsorted) {}
}

The compiler will perform constant folding and dead code elimination (if those optimisations are enabled, of course), and the result will be exactly the same as if you used the hypothetical static_if.

like image 112
syam Avatar answered Oct 14 '22 01:10

syam


I am afraid there has been a misunderstanding about the usage of static_if.

Certainly you can use static_if (or whatever trick you wish really) to try and get some optimization, but that is not its first goal.

The first goal of static_if is semantical. Let me demonstrate this with std::advance. A typical implementation of std::advance will use a type switch to choose, at compile time, between an O(1) implementation (for Random Access Iterators) and an O(n) implementation (for the others):

template <typename It, typename D>
void advance_impl(It& it, D d, random_access_iterator_tag)
{
    it += d;
}

template <typename It, typename D>
void advance_impl(It& it, D d, bidirectional_iterator_tag)
{
    if (d > D(0)) { for (D i(0); i < d; ++i) { ++it; } }
    else          { for (D i(0); i > d; --i) { --it; } }
}

template <typename It, typename D>
void advance_impl(It& it, D d, input_iterator_tag)
{
    for (D i(0); i < d; ++i) { ++it; }
}

And finally:

template <typename It, typename D>
void advance(It& it, D d)
{
    typename std::iterator_traits<It>::iterator_category c;
    advance_impl(it, d, c);
}

Why not use just a if in this case ? Because it would not compile.

  • a Bidirectional Iterator does not support +=
  • an Input Iterator (or Forward Iterator) does not support --

Thus, the only way to implement the functionality is to statically dispatch to a function only using the available operations on the given type.

like image 25
Matthieu M. Avatar answered Oct 14 '22 00:10

Matthieu M.