Strange error with type traits in VS2015

456 Views Asked by At

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();
    }
}
1

There are 1 best solutions below

8
On BEST ANSWER

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> );