Raghavendar Reddy
Raghavendar Reddy

Reputation: 99

Defining custom types in template metaprogramming

I found the following code in some video and I got very interested.

#include <iostream>

using namespace std;

#define LIST1(T1) Node<T1,Null>
#define LIST2(T1,T2) Node<T1,LIST1(T2)>
#define LIST3(T1,T2,T3) Node<T1,LIST2(T2,T3)>
#define LIST4(T1,T2,T3,T4) Node<T1,LIST3(T2,T3,T4)>

struct Null { };
template <int X, typename Next>
struct Node {
    static const int val = X;
    typedef Next Next_;
};

template <typename List> 
struct Sum
{
    static const int sum = List::val + Sum<typename List::Next_>::sum;
};

template <>
struct Sum<Null> {
    static const int sum = 0;
};

int main()
{
    cout << Sum<LIST4(2, 2, 3, 4)>::sum << endl;
}

My question is, what is the better way to do it? What other means do I have to do similar kind of parsing? The main problem I see is with the preprocessor directive used to define the type. Is there any other way to do so?

For example, I want to represent IP address same as above:

Sum<IP<120.100.10.20>> 

which will parse the input.

Also if it's possible to take it from file, it would be much more useful. I think this kind of formatted data should be allowed to taken as type in TMP. How would I modify the code to support that?

Edit :: Adding another code which make me feel it will be useful.

#include <iostream>
#define LIST1(T1) Node<T1,Null>
#define LIST2(T1,T2) Node<T1,LIST1(T2)>
#define LIST3(T1,T2,T3) Node<T1,LIST2(T2,T3)>
#define LIST4(T1,T2,T3,T4) Node<T1,LIST3(T2,T3,T4)>

using namespace std;

// Highest bit in an IP address
template <typename List, int N, bool B>
struct highestBitSet
{
    static const bool b = List::val & (1 << N);
    static const int bit = highestBitSet<List, N - 1, b>::bit;
};

template <typename List, int N>
struct highestBitSet<List,N,true>
{
    static const int bit = N+1;
};

template <typename List>
struct highestBitSet<List, -1, false>
{
    static const int bit = 8+highestBitSet<List::Next_, 7, false>::bit;
};

template <int X>
struct highestBitSet<Null, X, false>
{
    static const int bit = 0;
};


int main()
{
    // Will print 0 
    cout << highestBitSet<LIST4(1, 0, 0, 0), 7, false>::bit << endl;
    // WIll print 31
    cout << highestBitSet<LIST4(0, 0, 0, 255), 7, false>::bit << endl;
    return 0;
}

Upvotes: 0

Views: 81

Answers (1)

Jonathan Wakely
Jonathan Wakely

Reputation: 171453

My question is, what is the better way to do it? What other means do I have to do similar kind of parsing? The main problem I see is with the preprocessor directive used to define the type. Is there any other way to do so?

Using C++17's fold expressions, or C++14 constexpr if C++17 not available:

#include <iostream>

template<int... N> int_list { };

template<int... N> int_list<N...> list() { return {}; }

template<int... N>
int sum(int_list<N...>)
{
#if __cplusplus > 201402L
  return (N + ...);
#else
  const int vals[] = { N... };
  int sum = 0;
  for (auto v : vals)
    sum += v;
  return sum;
#endif
}

int main()
{
  std::cout << sum( list<2, 2, 3, 4>() ) << std::endl;
}

For example, I want to represent IP address same as above:

  Sum<IP<120.100.10.20>>

which will parse the input.

You don't need a variable length list for an IPv4 address, it always has four components. And the code you showed doesn't do any parsing ... that would be a completely different problem. Passing four separate arguments to a macro or function like LIST4(120, 100, 10, 20) is completely different to writing 120.100.10.20 which isn't even a valid C++ token.

Also if it's possible to take it from file, it would be much more useful. I think this kind of formatted data should be allowed to taken as type in TMP. How would I modify the code to support that?

Reading data from a file is a run-time effect, you can't combine templates (compile-time) with I/O (run-time).

Upvotes: 3

Related Questions