2

I've spent the past ~week developing a type-agnostic logging system for a game engine, and I'm using type traits to control how different template arguments should be recorded. Union and class types are ignoring those checks (somehow) and triggering error C2679.

I'd like to know why this might be happening and what I can do to fix it besides breaking my template down into lots of small functions.

if (std::is_arithmetic<loggableType>::value) { if (destination == DESTINATIONS::CONSOLE) { *printStreamPttr << "logging " << typeid(loggableType).name() << " with value " << dataLogging << '\n'; ConsolePrinter::OutputText(printStreamPttr); } else { logFileStream.open(logFilePath); logFileStream << "logging " << typeid(loggableType).name() << " with value " << dataLogging << '\n'; logFileStream.close(); } } 
6
  • 4
    This is not a compiler bug. if is a runtime construct, not compile-time. Code still has to be well-formed even if it is dead. For fixes, search for something like "static if emulation" or "tag dispatch". Commented Jan 5, 2017 at 3:43
  • so the problem is definitely somewhere in the compiler. -- If you're going to make claims like that, you need to post a complete example, with the reason why their is a compiler bug, examples of compilers that allow the code, quotes from the standard that permits the code to compile without error, etc. Commented Jan 5, 2017 at 4:02
  • Sorry, I think I might have just misunderstood - I assumed that an error showing up before the program started must be a compiler error, which is probably a bit silly. I've removed those statements from the question. Commented Jan 5, 2017 at 4:22
  • so whats the question after all? Commented Jan 5, 2017 at 4:29
  • Why is VS's error checker ignoring the if(std::is_arithmetic<>::value) check and giving me an error? How can I force it to see the if statement and actually compile my program? Commented Jan 5, 2017 at 4:32

1 Answer 1

4

if does not cause a compile-time branch. Both branches must be valid regardless of the result of the condition.

Here is my dispatch function I find useful in these cases:

template<std::size_t I> using index_t = std::integral_constant<std::size_t, I>; constexpr index_t<0> dispatch_index() { return {}; } template<class B0, class...Bools, class=std::enable_if_t<B0::value> > constexpr index_t<0> dispatch_index(B0, Bools...) { return {}; } template<class B0, class...Bools, class=std::enable_if_t<!B0::value> > constexpr auto dispatch_index(B0, Bools...bools) { return index_t< dispatch_index(bools...)+1 >{}; } template<class...Bools> constexpr auto dispatch( Bools...bools ) { using get_index = decltype(dispatch_index(bools...)); return [](auto&&...args){ using std::get; return get< get_index::value >( std::forward_as_tuple( decltype(args)(args)... ) ); }; } 

This utility function does compile-time dispatching between a set of options.

Here is an example:

 union bob {}; bob b; dispatch( std::is_arithmetic<decltype(b)>{} ) ( [&](auto&& value) { std::cout << value << "\n"; }, [&](auto&& value) { std::cout << "not a number\n"; } ) (b); 

dispatch( std::is_arithmetic<decltype(b)>{} ) takes a truthy or falsy type by value (actually any number of them). It finds the first truthy type passed to it.

It then returns a lambda that takes any number of arguments, and returns the one corresponding to the first truthy argument to dispatch. If dispatch has no truthy arguments, it pretends it had a truthy argument "after the end" and dispatches based on that.

 dispatch( std::is_arithmetic<decltype(b)>{} ) 

As bob is not is_arithmetic, this is a falsy type. So dispatch will return a lambda returning its second argument.

 ( [&](auto&& value) { std::cout << value << "\n"; }, [&](auto&& value) { std::cout << "not a number\n"; } ) 

In this case we pass it two lambdas, both with the same signature.

We are going to return the 2nd one.

 (b); 

We then pass it b. The first lambda, which expects the value to be passed to ostream&::operator<<, is never evaluated, so the fact that union bob doesn't support it doesn't matter.

live example, and using MSVC2015.

The above is valid C++14, and I believe I avoided all of the MSVC foibles that prevent it from compiling it.

To pass a type into your lambdas, you might want to use these:

template<class T>struct tag_t{using type=T; constexpr tag_t(){};}; template<class T>constexpr tag_t<T> tag{}; template<class Tag>using type_t = typename Tag::type; #define GET_TAGGED_TYPE(...) type_t< std::decay_t<decltype(__VA_ARGS__)> >; 

then your code looks like:

dispatch(std::is_arithmetic<loggableType>{}) ( [&](auto tag){ using loggableType=GET_TAGGED_TYPE(tag); if (destination == DESTINATIONS::CONSOLE) { *printStreamPttr << "logging " << typeid(loggableType).name() << " with value " << dataLogging << '\n'; ConsolePrinter::OutputText(printStreamPttr); } else { logFileStream.open(logFilePath); logFileStream << "logging " << typeid(loggableType).name() << " with value " << dataLogging << '\n'; logFileStream.close(); } }, [&](auto non_arith_tag) { // nothing? } ) ( tag<loggableType> ); 
Sign up to request clarification or add additional context in comments.

8 Comments

Thanks for the quick answer; I tried to implement your answer in my logger, and some weird errors appeared that didn't show up in your Coliru example: All the [&](auto&& value) calls raise errors on the subscript operators and the curly braces "dispatch_index does not take 1 arguments" (dispatch_index is totally unmodified from your example)
@paul what compiler, exactly? Version?
VS help says Clang with Microsoft Codegen
@paul well, what if you copy paste exactly my live example and nothing else? If so, provide the exact and complete error message (not a summary). The exact version of clang with microsoft codegen may also be useful; I think is based off clang 3.7? I have got a version of dispach to compile on it, but I don't have an online compiler to see what I would have to tweak. Another possibility is the compile time bool type does not have a ::value field like I require. You pass std::is_arithmetic<loggableType> not std::is_arithmetic<loggableType>:::value to dispatch.
Ok, just cleaned everything up (including swapping ::value out for the trait itself) and now it works. Thanks for the answer! :D I think I'm using a later version of clang? The "about VS" window says it's 14.0.25516, but idk if that's the version number or something else :|
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.