模板铸造问题

时间:2009-10-04 16:44:47

标签: c++ lua

当我尝试转换为类T的模板时,当T为float类型时,我似乎在下面的代码中出现错误。我已经意识到一种int函数正确,因为以下是有效的语法:

char* str = "3";
int num = (int)str;

浮动也是如此。我想知道是否有办法阻止g ++编译器在类型不匹配时出错,因此我可以使用RTTI方法typeid()来处理它。

class LuaConfig {
    // Rest of code omitted...

    // template currently supports both string and int
    template <class T> T getC(const char *key) {
        lua_pushstring(luaState, key);
        lua_gettable(luaState, -2);
        if (!lua_isnumber(luaState, -1)) {
            // throw error
            std::cout << "NOT A NUMBER" << std::endl;
        }

        T res;
        // WHERE THE PROBLEM IS:
        if (    typeid(T) == typeid(int)
             || typeid(T) == typeid(float)
        ) {
            std::cout << "AS NUM" << std::endl;
            // Floats should fall in here, but never does because of the
            // else clause failing at compile time.
            res = (T)lua_tonumber(luaState, -1);
        } else {
            // TODO: Fails on float here, it should fall down the
            // first branch (above). This branch should only be for string data.
            std::cout << "AS STRING" << std::endl;
            res = (T)lua_tostring(luaState, -1);        // LINE THAT CAUSES ISSUE.
        }

        std::cout << "OUT:" << res << std::endl;

        lua_pop(luaState, 1);
        return res;
    }
}

int main( int argc, char* args[] ) {
    LuaConfig *conf = new LuaConfig();
    std::cout << conf->getC<int>("width") << std::endl;
    std::cout << conf->getC<float>("width") << std::endl;       // This causes the error.
}

g ++抛出的错误是:

source/Main.cpp:128: error: invalid cast from type ‘char*’ to type ‘float’

6 个答案:

答案 0 :(得分:2)

尽量避免使用C风格的演员表。如果你写(int)ptr,其中ptr是一些指针,这将是一个reinterpret_cast,这可能不是你想要的。要将数字转换为字符串并再返回,请查看各种常见问题解答一种方法是使用std::stringstream类。

C风格的演员阵容很危险,因为它可以用于很多事情,而且它的作用并不总是很明显。 C ++提供了替代方案(static_cast,dynamic_cast,const_cast,reinterpret_cast)和功能样式的转换,相当于静态转换。

在(int)ptr的情况下,它将指针转换为int,不是指针指向的数字的字符串表示。

您可能还想查看Boost's lexical_cast

编辑:不要使用typeid。您可以在编译时完全处理这个问题:

template<typename T> struct doit; // no definition
template<> struct doit<int> {
    static void foo() {
        // action 1 for ints
    }
};
template<> struct doit<float> {
    static void foo() {
        // action 2 for floats
    }
};

....

template<typename T> void blah(T x) {
    // common stuff
    doit<T>::foo(); // specific stuff
    // common stuff
}

如果T既不是int也不是float,则会出现编译时错误。我希望你明白这一点。

答案 1 :(得分:1)

您需要在编译时进行分支。将模板中的内容更改为以下内容:

   template<typename T> struct id { };

   // template currently supports both string and int
    template <class T> T getC(const char *key) {
        lua_pushstring(luaState, key);
        lua_gettable(luaState, -2);
        if (!lua_isnumber(luaState, -1)) {
            // throw error
            std::cout << "NOT A NUMBER" << std::endl;
        }

        T res = getCConvert(luaState, -1, id<T>())
        std::cout << "OUT:" << res << std::endl;

        lua_pop(luaState, 1);
        return res;
    }

    // make the general version convert to string
    template<typename T>
    T getCConvert(LuaState s, int i, id<T>) { 
      return (T)lua_tostring(s, i); 
    }

    // special versions for numbers
    float getCConvert(LuaState s, int i, id<int>) { 
      return (float)lua_tonumber(s, i); 
    }

    int getCConvert(LuaState s, int i, id<float>) { 
      return (int)lua_tonumber(s, i); 
    }

有几个alternative ways可以解决这个问题。为避免重复添加重载,boost::enable_if可能很有用。但只要你只有两个int和float的特殊情况,我会保持简单,只需重复一次调用lua_tonumber

避免enable_if并且仍然避免重复重载的另一种模式是引入类型标志的层次结构 - 将id更改为以下内容,并使getC内的代码保持不变以上。如果有更多需要特殊处理的案例,我会使用它:

template<typename T> struct tostring { };
template<typename T> struct tonumber { };

template<typename T> struct id : tostring<T> { };
template<> struct id<int> : tonumber<int> { };
template<> struct id<float> : tonumber<float> { };

id现在需要在类模板之外定义,因为您无法在模板中明确地对其进行专门化。然后将辅助函数的重载更改为以下

    // make the general version convert to string
    template<typename T>
    T getCConvert(LuaState s, int i, tostring<T>) { 
      return (T)lua_tostring(s, i); 
    }

    // special versions for numbers
    template<typename T>
    T getCConvert(LuaState s, int i, tonumber<T>) { 
      return (T)lua_tonumber(s, i); 
    }

然后,专业化将确定应该使用字符串和数字转换的“配置”。

答案 2 :(得分:0)

我不熟悉Lua,但在这种情况下我认为不重要......

lua_toString的返回显然是char*,这意味着您获取值的地址,然后尝试将该地址转换为浮点数。请查看strtod以了解如何更正确地执行此操作,或者,正如sellibitze所指出的,使用字符串流。

答案 3 :(得分:0)

我以前从未接触过lua或它的引擎,但在我看来,你误导了lua_tostring。这是它的签名:

const char *lua_tostring (lua_State *L, int index);

显然,此函数返回const char*。如果是T == int,则C / C ++允许从reinterpret_castpointer的所谓intT == float的情况下,这种转换毫无意义。我认为您必须获取返回的c字符串,然后根据类型将其转换为使用atoiatof的数字。问题发生在这里:

res = (T)lua_tonumber(luaState, -1);

因为正如我们所说,指针可以在C / C ++中以有意义的方式转换为integers,与floats不同。

答案 4 :(得分:0)

使用memcpy()进行赋值可以避免编译错误。

char *str = lua_tostring(luaState, -1)
memcpy(&res, &str, sizeof(res));

但是,lua_tostring()返回的字符串在调用lua_pop()后不再有效。该字符串确实需要复制到另一个缓冲区。

答案 5 :(得分:0)

尽管Lua网站说的是,并且它具有逻辑意义,但在测试时我发现指针在状态关闭后仍然有效。虽然他是对的,如果你想在Lua函数返回后保留它,你真的应该复制字符串,这可能不是他的问题。