通过函数指针传递给可变函数的参数改变它们的值

时间:2016-12-16 14:56:38

标签: c++ c++11 function-pointers

我有一个带有一些静态函数的类和一个包含指向这些函数的指针的映射:

class Conditions
{
    using cbType = bool(*)();
    static std::unordered_map<std::string, cbType> const m_FunctionMap;

    static bool Equal_int(const int A, const int B) { return A == B; }
    static bool Equal_float(const float A, const float B) { return A == B; }
    static bool Greater_int(const int A, const int B) { return A > B; }
    static bool Greater_float(const float A, const float B) { return A > B; }
    static bool Between_int(const int A, const int B, const int C) { return A > B && A < C; }
    static bool Between_float(const float A, const float B, const float C) { return A > B && A < C; }
};

因此,静态函数可以具有不同类型的不同数量的参数。在.cpp文件中,我正在初始化该映射:

std::unordered_map<std::string, Conditions::cbType> const Conditions::m_FunctionMap
{
    { "Equal_int", MakeMapVal(&Equal_int) },
    { "Equal_float", MakeMapVal(&Equal_float) },
    { "Greater_int", MakeMapVal(&Greater_int) },
    { "Greater_float", MakeMapVal(&Greater_int) },
    { "Between_int", MakeMapVal(&Between_int) },
    { "Between_float", MakeMapVal(&Between_float) },
};

然后我向类Conditions添加了一个方法,用它们的名称来调用这些静态函数:

    template <typename ... argsType>
    static bool Call(std::string const& Key, argsType&& ... Args)
    {
        using prototype = bool(*)(argsType ...);
        return reinterpret_cast<prototype>(m_FunctionMap.at(Key))(Args ...);
        //return reinterpret_cast<prototype>(m_FunctionMap.at(Key))(std::forward<argsType>(Args) ...);  // this has the same issue
    }

现在,当我运行此代码时,请调用正确调用相应静态函数的Call(std::string const& Key, argsType&& ... Args)方法。例如:

Call("Greater_int", 42, 40);

但是,在Greater_int()函数中,这两个参数不再是42和40,而是一些随机值。但在Call函数中,这些值正确4240。通过reinterpret_cast调用函数指针时,这些值会发生变化。

我在这里做错了什么想法?

2 个答案:

答案 0 :(得分:2)

你处于未定义行为的荒野中。你的reinterpret_cast来自不相关的类型,这很糟糕。删除强制转换并让编译器以其他方式开心。

答案 1 :(得分:0)

除非我误解了,否则你想创建一个允许解析和比较不同类型的DSL。

使用boost::variant可以非常轻松地完成此操作。它只是一些样板和定义不同类型的比较操作的问题。

小而完整的例子:

#include <boost/variant.hpp>
#include <boost/operators.hpp>
#include <string>
#include <iostream>
#include <iomanip>

// define the concept of equality in my scripting language
struct is_equal : boost::static_visitor<bool>
{
    // x == x is easy
    template<class T>
    bool operator()(const T& l, const T& r) const {
        return l == r;
    }

    // define the concept of comparing strings to integers
    bool operator()(const std::string& l, const int& r) const {
        return l == std::to_string(r);
    }

    // and integers to strings
    bool operator()(const int& l, const std::string& r) const {
        return (*this)(r, l);
    }
};

struct is_less : boost::static_visitor<bool>
{
    // x == x is easy
    template<class T>
    bool operator()(const T& l, const T& r) const {
        return l < r;
    }

    // define the concept of comparing strings to integers
    bool operator()(const std::string& l, const int& r) const {
        return l < std::to_string(r);
    }

    // and integers to strings
    bool operator()(const int& l, const std::string& r) const {
        return (*this)(r, l);
    }
};

struct emit : boost::static_visitor<std::ostream&>
{
    emit(std::ostream& os) : os_(os) {}

    // x == x is easy
    template<class T>
    std::ostream& operator()(const T& l) const {
        return os_ << l;
    }

    std::ostream& operator()(const std::string& s) const {
        return os_ << std::quoted(s);
    }

    std::ostream& os_;
};

struct scriptable_value
: boost::less_than_comparable<scriptable_value>
, boost::equality_comparable<scriptable_value>
{
    using variant_type = boost::variant<std::string, int>;

    scriptable_value(std::string v) : variant_(std::move(v)) {}
    scriptable_value(int v) : variant_(v) {}

    variant_type const& as_variant() const {
        return variant_;
    }

private:
    variant_type variant_;
};

bool operator==(scriptable_value const& l, scriptable_value const& r)
{
    return boost::apply_visitor(is_equal(), l.as_variant(), r.as_variant());
}

bool operator<(scriptable_value const& l, scriptable_value const& r)
{
    return boost::apply_visitor(is_less(), l.as_variant(), r.as_variant());
}

std::ostream& operator<<(std::ostream& os, scriptable_value const& r)
{
    return boost::apply_visitor(emit(os), r.as_variant());
}


int main()
{
    auto x = scriptable_value(10);
    auto y = scriptable_value("10");
    auto x2 = scriptable_value(9);
    auto y2 = scriptable_value("9");

    std::cout << x << " == " << y << " : " << std::boolalpha << (x == y) << std::endl;
    std::cout << x << " != " << y << " : " << std::boolalpha << (x != y) << std::endl;
    std::cout << x << " == " << y2 << " : " << std::boolalpha << (x == y2) << std::endl;
    std::cout << x << " != " << y2 << " : " << std::boolalpha << (x != y2) << std::endl;

    std::cout << x << " <  " << y << " : " << std::boolalpha << (x < y) << std::endl;
    std::cout << x << " >= " << y << " : " << std::boolalpha << (x >= y) << std::endl;
    std::cout << x << " <  " << y2 << " : " << std::boolalpha << (x < y2) << std::endl;
    std::cout << x << " >= " << y2 << " : " << std::boolalpha << (x >= y2) << std::endl;

    std::cout << x << " == " << x2 << " : " << std::boolalpha << (x == x2) << std::endl;
    std::cout << x << " != " << x2 << " : " << std::boolalpha << (x != x2) << std::endl;

}

预期产出:

10 == "10" : true
10 != "10" : false
10 == "9" : false
10 != "9" : true
10 <  "10" : false
10 >= "10" : true
10 <  "9" : false
10 >= "9" : true
10 == 9 : false
10 != 9 : true