Metaprogram stops reporting at the first error

Hi,

I copied Erwin Unruh's first metaprogram to print primes using Visual Studio 2017 latest version (15.6.2).

I also ran it in metashell with the same response - so it makes me think it is not a compiler limitation (or not only a Visual C++ limitation).

For clarity, I will copy here the code for that ominous first metaprogram:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
template<int p, int i>
struct is_prime
{
	enum { pri = (p == 2) || ((p%i) && is_prime< ((i > 2) ? p : 0), i - 1 >::pri) };
};

template<>
struct is_prime<0, 0>
{
	enum { pri = 1 };
};

template<>
struct is_prime<0, 1>
{
	enum { pri = 1 };
};

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>	Used to generate error when D is attempted to be 
/// 				initialized with a non-pointer or an integer that is not zero
/// 				(only integer convertible to void* is 0) </summary>
///
/// <remarks>	Juan Dent, 16/3/2018. </remarks>
///
/// <typeparam name="i">	Generic type parameter. </typeparam>
////////////////////////////////////////////////////////////////////////////////////////////////////

template<int i>
struct D
{
	D(void*);
};

template<int i>
struct CondNull
{
	static int const value = 1;
};

template<>
struct CondNull<0>
{
	static void* value;
};

void* CondNull<0>::value = 0;



template<int i>
struct Prime_print
{
	Prime_print<i - 1> a;							// recursive (requires Prime_print<1> specialization)
	enum { pri = is_prime<i, i - 1>::pri };
	void f()
	{
		D<i> d = CondNull<pri ? 1 : 0>::value;		// 1 ==> error, 0 ==> succeeds
		a.f();
	}
};

template<>
struct Prime_print<1>		// full specialization to end the loop
{
	enum { pri = 0 };		// why is this needed??
	void f()
	{
		D<1> d = 0;
	}
};

constexpr int const LAST = 18;

void print_primes()
{
	Prime_print<LAST> a;
	a.f();
}


Compiling this gives these errors:


meta_erwinunruh.cpp(76,1): error C2440: 'initializing': cannot convert from 'const int' to 'D<17>'
		D<i> d = CondNull<pri ? 1 : 0>::value;
meta_erwinunruh.cpp(75): note: while compiling class template member function 'void Prime_print<17>::f(void)'


According to the book C++ Templates the complete guide, second edition, the compiler should print an error for EACH prime number found in the range [1,LAST], but my compiler stops at the first (in this case it is 17).

As I said, metashell also stops at the first error so it does not appear to be a Visual C++ problem.

Thanks for any insights...


Regards,
Juan Dent
Hi Juan,

I have that same book :+)

I compiled with clang++ and turned off the option in the IDE (Eclipse) to stop building on first error, as per the footnote 7 on page 546 (Chapter 23). There were several pages of output, but if one only looks at the errors, indeed it is a list of primes.

Hope this helps

Regards :+D

Edit:

Also, important not to use any sort of make

Next, might have to get the book "C++ Template Metaprogramming - Concepts, Tools, and Techniques From Boost and Beyond" by David Abrahams and Alesky Gurtovoy
Last edited on
Hi TheIdeasMan,

Thank you - sounds right on the money!

Will test it.

Do have that other book as well :-)


Regards,
Juan Dent
Unfortunately it did not work! Visual C++ works the same whether I check "Stop build on first error"or not. It just seems unable to continue after the initialization of the variable d in the function Prime_print<i>::f() fails. It probably causes the compiler to loose track of what it is doing.

I was thinking to report each prime via static_assert and constexpr if like so:


1
2
3
4
5
6
7
8
void f()
{
	if constexpr (pri)
	{
		static_assert(false, iToCompileTimeStr<i>() );
	}
	a.f();
}


where iToCompileTimeStr is a constexpr functioon that transforms the integer i into a const char array representation. This might work...???



Anybody knows of such a function?

Regards,
Juan Dent

Hi,

I propose this function to convert an integer into a char array at compile time. It still has errors but maybe another pair of eyes can shed some light to the problem.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
template<int i, char* buffer, int N>
struct SetDigit
{
	constexpr static void doIt()
	{
		int digit = i % 10;
		buffer[N] = digit + '0';
		SetDigit<i / 10, buffer, N-1>::doIt();
	}
};

template<char* buffer, int N>
struct SetDigit<0,buffer, N>
{
	constexpr static void doIt()
	{
		buffer[N]= ' ';
		SetDigit<0, buffer, N-1>::doIt();
	}
};

template<char* buffer>
struct SetDigit<0, buffer, 0>
{
	constexpr static void doIt()
	{
		buffer[0] = ' ';
	}
};
[/coded]


This seems to work. Now we need the one that calls the initial SetDigit:

[code]
struct IntToStr
{
	constexpr static std::size_t MAX = 10;
	static inline char buffer[MAX];


	template<int i>
	constexpr static char* Convert()
	{
		buffer[MAX - 1] = 0;
		SetDigit<i, buffer, MAX - 2>::doIt();
		return buffer;
	}
};



This works as in:

 
char* res = IntToStr::Convert<17>();


But does not work in:

 
static_assert(true, IntToStr::Convert<15>());


The compiler complains :


expected a string literal



Thanks for any help

Juan
Hi,

The awkward thing about this is that the Unruh's code produced errors that happened to demonstrate that compile computation could be performed. But IMO that is counter to what we would normally do with any template programming.

http://en.cppreference.com/w/cpp/language/static_assert
cppreference wrote:

Since message has to be a string literal, it cannot contain dynamic information or even a constant expression that is not a string literal itself. In particular, it cannot contain the name of the template type argument.


As an aside, the static_assert message prints when the first argument evaluates to false.

With the original code, I could not get g++ to produce all the errors either, tried different compiler flags, maybe something in specs, but I couldn't see it. So you are not alone in not getting to VS to work. Maybe there is some other compiler option in VS?

What did work for me, in a bash shell on Linux:

$ clang++ -std=c++17 main.cpp 2> clangout
$ cat clangout | grep error


main.cpp:58:8: error: no viable conversion from 'const int' to 'D<17>'
                D<i> d = CondNull<pri ? 1 : 0>::value;          // 1 ==> error, 0 ==> succeeds
main.cpp:58:8: error: no viable conversion from 'const int' to 'D<13>'
                D<i> d = CondNull<pri ? 1 : 0>::value;          // 1 ==> error, 0 ==> succeeds
main.cpp:58:8: error: no viable conversion from 'const int' to 'D<11>'
                D<i> d = CondNull<pri ? 1 : 0>::value;          // 1 ==> error, 0 ==> succeeds
main.cpp:58:8: error: no viable conversion from 'const int' to 'D<7>'
                D<i> d = CondNull<pri ? 1 : 0>::value;          // 1 ==> error, 0 ==> succeeds
main.cpp:58:8: error: no viable conversion from 'const int' to 'D<5>'
                D<i> d = CondNull<pri ? 1 : 0>::value;          // 1 ==> error, 0 ==> succeeds
main.cpp:58:8: error: no viable conversion from 'const int' to 'D<3>'
                D<i> d = CondNull<pri ? 1 : 0>::value;          // 1 ==> error, 0 ==> succeeds
main.cpp:58:8: error: no viable conversion from 'const int' to 'D<2>'

Topic archived. No new replies allowed.