Reputation: 99
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
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