Search code examples
c++c++17variant

Why does using a int64_t error here and an int compiles?


Help out a brother. This code compiles, does what I want.

#include <iostream>
#include <unordered_map>
#include <variant>
#include <vector>
#include <random>
#include <ctime>

class GlobalVariables
{
public:
    typedef std::wstring VariableNameType;


    /***************************************************************
    ----------------------------------------------------------------------

    Important that my std::variant holds an int

    -----------------------------------------------------------------
    *****************************************************************/


    typedef std::variant<bool,int,float,std::wstring> VariableType;
    typedef std::unordered_map<VariableNameType,VariableType> VariableContainerType;

    static const VariableType& GetValue(const VariableNameType& name) { return m_variables[name]; }
    static void SetValue(const VariableNameType& name, const VariableType value) { m_variables[name] = value; }

protected:
    static VariableContainerType m_variables;
};

GlobalVariables::VariableContainerType GlobalVariables::m_variables;

class RandomNumber
{
public:
    typedef std::mt19937 EngineType;

    static void Seed(const EngineType::result_type seed)
    {
        m_engine.seed(seed);
    }

protected:
    static EngineType m_engine;
};

RandomNumber::EngineType RandomNumber::m_engine(std::time(0));

template<typename T>
class RandomNumberGenerator : public RandomNumber
{ };

template<>
class RandomNumberGenerator<int> : public RandomNumber
{
public:
    RandomNumberGenerator(const int min, const int max)
    :
        m_intDistro(min,max)
    {}

    int operator()() { return m_intDistro(m_engine); }

protected:

    std::uniform_int_distribution<int> m_intDistro;
};

template<>
class RandomNumberGenerator<float> : public RandomNumber
{
public:
    RandomNumberGenerator(const float min, const float max)
    :
        m_floatDistro(min,max)
    {}

    float operator()() { return m_floatDistro(m_engine); }

protected:

    std::uniform_real_distribution<float> m_floatDistro;
};


enum class COMPARISON_OP : uint8_t { EE, NE, LT, GT, LE, GE };
enum class BINARY_OP : uint8_t { ADD, SUB, MUL, DIV };

class Expression
{
public:
    virtual GlobalVariables::VariableType GetValue() const = 0;
};

class Constant : public Expression
{
public:
    virtual GlobalVariables::VariableType GetValue() const
    {
        return value;
    }

    GlobalVariables::VariableType value;
};

class Identifier : public Expression
{
public:
    virtual GlobalVariables::VariableType GetValue() const override
    {
        return GlobalVariables::GetValue(name);
    }

    GlobalVariables::VariableNameType name;
};

class System : public Expression
{
public:
    virtual GlobalVariables::VariableType GetValue() const override
    {
        return GlobalVariables::GetValue(name);
    }

    GlobalVariables::VariableNameType name;
};



class BinaryOp : public Expression
{
public:
    virtual GlobalVariables::VariableType GetValue() const override
    {
        if(op == BINARY_OP::ADD)



    /***************************************************************
    ----------------------------------------------------------------------

    Important that my visitor returns a simple addition if the types are the 
    same

    -----------------------------------------------------------------
    *****************************************************************/



            return std::visit([](auto a, auto b) -> GlobalVariables::VariableType
            {
                if constexpr(!std::is_same_v<decltype(a),decltype(b)>)
                    throw;
                else
                    return a + b;
            }, lhs->GetValue(), rhs->GetValue());
        else if(op == BINARY_OP::SUB)
            return 0;
        else if(op == BINARY_OP::MUL)
            return 0;
        else // Division
            return 0;
    }

    BINARY_OP op;
    Expression* lhs;
    Expression* rhs;
};

class Comparison
{
public:
    bool GetValue() const
    {
        if(op == COMPARISON_OP::EE)
            return lhs->GetValue() == rhs->GetValue();
        else if(op == COMPARISON_OP::NE)
            return lhs->GetValue() != rhs->GetValue();
        else if(op == COMPARISON_OP::LT)
            return lhs->GetValue() < rhs->GetValue();
        else if(op == COMPARISON_OP::GT)
            return lhs->GetValue() > rhs->GetValue();
        else if(op == COMPARISON_OP::LE)
            return lhs->GetValue() <= rhs->GetValue();
        else // Greater or Equal to
            return lhs->GetValue() >= rhs->GetValue();
    }

    COMPARISON_OP op;
    Expression* lhs;
    Expression* rhs;
};

class Random : public Expression
{
public:
    virtual GlobalVariables::VariableType GetValue() const override
    {
        return possibilities[randomSelector()]->GetValue();
    }

    mutable RandomNumberGenerator<int> randomSelector;

    std::vector<Expression*> possibilities;
};

class Option
{

};

class Event
{
public:
    Expression* eventText;
};

class Branch
{
public:
    const std::wstring& GetValue() const
    {
        if(condition->GetValue())
            return trueBody;
        else
            return falseBody;
    }

    Comparison* condition;
    std::wstring trueBody;
    std::wstring falseBody;
};

int main()
{

}

There's probably a lot of unnecessary code so I've highlighted what I believe to be the important parts.

But, if I change my std::variant to hold int64_t instead of an int (not "in addition to", but "instead of") i.e. typedef std::variant<bool,int64_t,float,std::wstring> VariableType the program fails to compile.

The compiler errors are quite long:

||=== Build: Debug in Space Adventure 2 (compiler: Mingw-7.3.0) ===|
main.cpp||In instantiation of 'BinaryOp::GetValue() const::<lambda(auto:1, auto:2)> [with auto:1 = bool; auto:2 = bool; GlobalVariables::VariableType = std::variant<bool, long long int, float, std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > >]':|
\include\c++\type_traits|2797|required from 'constexpr bool std::__call_is_nt(std::__invoke_other) [with _Fn = BinaryOp::GetValue() const::<lambda(auto:1, auto:2)>; _Args = {bool, bool}]'|
\include\c++\type_traits|2803|  required by substitution of 'template<bool __v> using __bool_constant = std::integral_constant<bool, __v> [with bool __v = std::__call_is_nt<BinaryOp::GetValue() const::<lambda(auto:1, auto:2)>, bool, bool>((std::__result_of_success<std::variant<bool, long long int, float, std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > >, std::__invoke_other>::__invoke_type{}, std::__result_of_success<std::variant<bool, long long int, float, std::__cxx11::basic_string<wchar_t, st|
\include\c++\type_traits|2801|required from 'struct std::__call_is_nothrow<std::__invoke_result<BinaryOp::GetValue() const::<lambda(auto:1, auto:2)>, bool, bool>, BinaryOp::GetValue() const::<lambda(auto:1, auto:2)>, bool, bool>'|
\include\c++\type_traits|143|required from 'struct std::__and_<std::__is_invocable<BinaryOp::GetValue() const::<lambda(auto:1, auto:2)>, bool, bool>, std::__call_is_nothrow<std::__invoke_result<BinaryOp::GetValue() const::<lambda(auto:1, auto:2)>, bool, bool>, BinaryOp::GetValue() const::<lambda(auto:1, auto:2)>, bool, bool> >'|
\include\c++\type_traits|2813|required from 'struct std::__is_nothrow_invocable<BinaryOp::GetValue() const::<lambda(auto:1, auto:2)>, bool, bool>'|
\include\c++\bits\invoke.h|89|  [ skipping 2 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]|
\include\c++\variant|691|required from 'static constexpr auto std::__detail::__variant::__gen_vtable_impl<std::__detail::__variant::_Multi_array<_Result_type (*)(_Visitor, _Variants ...)>, std::tuple<_Tail ...>, std::integer_sequence<long long unsigned int, __indices ...> >::_S_apply() [with _Result_type = std::variant<bool, long long int, float, std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > >; _Visitor = BinaryOp::GetValue() const::<lambda(auto:1, auto:2)>&&; _Variants = {std::variant<bo|
\include\c++\variant|656|  recursively required from 'static constexpr void std::__detail::__variant::__gen_vtable_impl<std::__detail::__variant::_Multi_array<_Result_type (*)(_Visitor, _Variants ...), __dimensions ...>, std::tuple<_Variants ...>, std::integer_sequence<long long unsigned int, __indices ...> >::_S_apply_single_alt(_Tp&) [with long long unsigned int __index = 0; _Tp = std::__detail::__variant::_Multi_array<std::variant<bool, long long int, float, std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::al|
\include\c++\variant|656|required from 'constexpr const std::__detail::__variant::_Multi_array<std::variant<bool, long long int, float, std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > > (*)(BinaryOp::GetValue() const::<lambda(auto:1, auto:2)>&&, std::variant<bool, long long int, float, std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > >&&, std::variant<bool, long long int, float, std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::alloc|
\include\c++\variant|709|required from 'struct std::__detail::__variant::__gen_vtable<std::variant<bool, long long int, float, std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > >, BinaryOp::GetValue() const::<lambda(auto:1, auto:2)>&&, std::variant<bool, long long int, float, std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > >&&, std::variant<bool, long long int, float, std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t|
\include\c++\variant|1245|required from 'constexpr decltype(auto) std::visit(_Visitor&&, _Variants&& ...) [with _Visitor = BinaryOp::GetValue() const::<lambda(auto:1, auto:2)>; _Variants = {std::variant<bool, long long int, float, std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > >, std::variant<bool, long long int, float, std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > >}]'|
main.cpp|133|required from here|
\include\c++\variant|709|  in constexpr expansion of 'std::__detail::__variant::__gen_vtable<std::variant<bool, long long int, float, std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > >, BinaryOp::GetValue() const::<lambda(auto:1, auto:2)>&&, std::variant<bool, long long int, float, std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > >&&, std::variant<bool, long long int, float, std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<|
\include\c++\variant|706|  in constexpr expansion of 'std::__detail::__variant::__gen_vtable_impl<std::__detail::__variant::_Multi_array<std::variant<bool, long long int, float, std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > > (*)(BinaryOp::GetValue() const::<lambda(auto:1, auto:2)>&&, std::variant<bool, long long int, float, std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > >&&, std::variant<bool, long long int, float, std::__cxx11::basic_string<wcha|
\include\c++\variant|646|  in constexpr expansion of 'std::__detail::__variant::__gen_vtable_impl<std::__detail::__variant::_Multi_array<std::variant<bool, long long int, float, std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > > (*)(BinaryOp::GetValue() const::<lambda(auto:1, auto:2)>&&, std::variant<bool, long long int, float, std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > >&&, std::variant<bool, long long int, float, std::__cxx11::basic_string<wcha|
\include\c++\variant|668|  in constexpr expansion of 'std::__detail::__variant::__gen_vtable_impl<std::__detail::__variant::_Multi_array<std::variant<bool, long long int, float, std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > > (*)(BinaryOp::GetValue() const::<lambda(auto:1, auto:2)>&&, std::variant<bool, long long int, float, std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > >&&, std::variant<bool, long long int, float, std::__cxx11::basic_string<wcha|
\include\c++\variant|646|  in constexpr expansion of 'std::__detail::__variant::__gen_vtable_impl<std::__detail::__variant::_Multi_array<std::variant<bool, long long int, float, std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > > (*)(BinaryOp::GetValue() const::<lambda(auto:1, auto:2)>&&, std::variant<bool, long long int, float, std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > >&&, std::variant<bool, long long int, float, std::__cxx11::basic_string<wcha|
main.cpp|132|error: could not convert '(((int)a) + ((int)b))' from 'int' to 'GlobalVariables::VariableType {aka std::variant<bool, long long int, float, std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > >}'|
main.cpp||In member function 'virtual GlobalVariables::VariableType BinaryOp::GetValue() const':|
main.cpp|135|error: could not convert '0' from 'int' to 'GlobalVariables::VariableType {aka std::variant<bool, long long int, float, std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > >}'|
main.cpp|137|error: could not convert '0' from 'int' to 'GlobalVariables::VariableType {aka std::variant<bool, long long int, float, std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > >}'|
main.cpp|139|error: could not convert '0' from 'int' to 'GlobalVariables::VariableType {aka std::variant<bool, long long int, float, std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > >}'|
||=== Build failed: 12 error(s), 10 warning(s) (0 minute(s), 1 second(s)) ===|

But I think the real key is this error:

\main.cpp|132|error: could not convert '(((int)a) + ((int)b))' from 'int' to 'GlobalVariables::VariableType {aka std::variant<bool, long long int, float, std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > >}'|

I think it's important because at no point in time am I using int, I'm using int64_t so it seems like the program has implicitly converted from int64_t down to an int and I can't see where or why or how to stop it.

Compiling with Mingw64 7.3.0.


Solution

  • The problems are with the return statements in the BinaryOp::GetValue function. The first error comes from the return a + b; line, when expanding the bool member of the variant. The two bool values are converted to int, these are added together, then that int is used to create a new GlobalVariables::VariableType value. However, there is no unambiguous conversion from int to VariableType.

    The other errors all come from the various return 0; statements for the same reason.

    The fixes are simple enough. Change the first return to

    return decltype(a)(a + b);
    

    which will convert the result of the addition to the same type as a, which is necessary for small types that get promoted to int before the addition happens (bool, char, short and their signed/unsigned variations).

    For the return 0; lines, you need to decide what type the 0 should be (bool? int_64? float?) and explicitly specify the type for that 0.