0

I've got problem with templates:

I have got two constructors and method: .cpp:

Cell::Cell(sf::Vector2i& uPos, sf::Vector2f& cellDimensions, std::string& stateName)
    :unitPosition(uPos)
{
    setBasicParameters<std::string>(stateName,cellDimensions,uPos);
}

Cell::Cell(sf::Vector2i & uPos, sf::Vector2f & cellDimensions, int stateNumber)
    :unitPosition(uPos)
{
    setBasicParameters<int>(stateNumber,cellDimensions,uPos);
}

.hpp::

//Basic parameters which are being used by constructors
    template < typename T = typename std::enable_if< std::is_base_of<int, T>::value, T>::type || typename std::enable_if< std::is_base_of<std::string, T>::value, T>::type>
    void setBasicParameters(T& object, sf::Vector2f& cellDimensions, sf::Vector2i& uPos);

template<typename T>
inline void Cell::setBasicParameters(T& object, sf::Vector2f& cellDimensions, sf::Vector2i& uPos)
{
    shape.setSize(cellDimensions);
    shape.setOutlineThickness(cellDimensions.x / 10.0f); //10%

    shape.setOutlineColor(constants::cell::FILL_COLOR);

    shape.setPosition(uPos.x*cellDimensions.x, uPos.y*cellDimensions.y);

    if (!StateSystem::isStateExist(object))
    {
        Logger::Log(constants::error::stateSystem::STATE_DOES_NOT_EXIST, Logger::STREAM::BOTH, Logger::TYPE::ERROR);
        state = StateSystem::getNumberOfState(constants::defaults::EMPTY);
    }
    else
    {
        if (std::is_base_of<std::string, T>::value)
            state = StateSystem::getNumberOfState(object);  
        else state = object;

        setColor(StateSystem::getColorOfState(state));
    }
}

and problem is there:

if (std::is_base_of<std::string, T>::value)
            state = StateSystem::getNumberOfState(object);  
        else state = object;

In this if, I check a type of T, and if it is std::string, I use method from StateSystem which changes name to number. In the other way, if T is int, I don't need to change it so I am immediately assign T to state(state is int). But my compiler checks the dwo options and gives me errors:

Severity    Code    Description Project File    Line    Suppression State

Error C2440 '=': cannot convert from 'std::string' to 'uint8_t'

Severity    Code    Description Project File    Line    Suppression State
Error   C2664   'int8_t mv::StateSystem::getNumberOfState(std::string)': cannot convert argument 1 from 'int' to 'std::string'  

Can I repair it without do two diffrent methods?

mvxxx
  • 115
  • 9

2 Answers2

4

The problem is that the if statement here...

if (std::is_base_of<std::string, T>::value)

...is a run-time branch. Even though is_base_of can be evaluated at compile-time, the compiler is forced to compile both branches of the if statement, even if their correctness relies on the is_base_of condition.


Can I repair it without two different methods?

C++17 introduces if constexpr (...), which does the branching at compile-time. This still requires both branches to be parseable, but only instantiates the one that matches the predicate. Therefore the non-taken branch can be "invalid" and your program will work as expected.

if constexpr (std::is_base_of<std::string, T>::value)
    state = StateSystem::getNumberOfState(object);  
else state = object;

If you do not have access to C++14 and you really don't want to use two different functions, you can implement an equivalent construct to if constexpr(...). The implementation requires a significant amount of boilerplate. The final result will look like this:

static_if(std::is_base_of<std::string, T>{})
    .then([&](auto){ state = StateSystem::getNumberOfState(object); })
    .else_([&](auto){ state = object; })(_);

I gave a talk at CppCon 2016 and Meeting C++ 2016 called "Implementing static control flow in C++14" which explains how static_if works and how to implement it yourself.


If you decide that using two different functions is acceptable, here's how you can solve the issue:

if (!StateSystem::isStateExist(object))
{
    // ...as before...
}
else
{
    dispatch(state, std::is_base_of<std::string, T>{});
    // ...as before...
}

Where dispatch is defined as:

void dispatch(State& state, Object& object, std::true_type)
{
    state = StateSystem::getNumberOfState(object);
}

void dispatch(State& state, Object& object, std::false_type)
{
    state = object;
}
Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • Question: what happens with sttic_if that if T is derivative of string. I may imagine that the meet same problem as with static_cast to base class (ending up wiith a temporal?) – Swift - Friday Pie Feb 22 '17 at 10:59
  • @Swift: sorry but I don't understand what you're asking - can you show a minimal code example? Regardless, `static_if`'s behavior should be equivalent to `if constexpr`, despite the less enticing syntax. – Vittorio Romeo Feb 22 '17 at 11:04
  • @VittorioRomeo, i have tried with if constexpr but it gave me 1 new error. I use Visual Studio 2015 Community https://scr.hu/6LXMY6 – mvxxx Feb 22 '17 at 11:57
  • @mvxxx: that requires C++17 support - check MSVC compliance on Visual Studio's website – Vittorio Romeo Feb 22 '17 at 12:10
  • well i cant yet figure how to implement it prior to 14 and video links ( as any other non-textual media) are useless to me – Swift - Friday Pie Feb 22 '17 at 12:50
  • @Swift: you can't implement it prior to 14, as *generic lambdas* are pretty much mandatory. – Vittorio Romeo Feb 22 '17 at 12:53
0

std::is_base_of is a compile-time thing. You should use it with static_assert, for example. Type information at runtime in C++ is something complicated and you should use stuff like RTTI or dynamic_cast for it.

This answer gives answers about the difference.

Community
  • 1
  • 1
The Quantum Physicist
  • 24,987
  • 19
  • 103
  • 189