2009-10-04 288 views
0

当我尝试投射到类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. 
} 

误差克++抛出是:

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

考虑解释你在这里要做的事情。请记住,大多数阅读此文的人可能并不知道lua_xxx函数返回的内容。 – sellibitze 2009-10-04 18:26:59

+0

我不认为typeid(int)或typeid(float)是C++中的有效构造。 typeid通常作为类的vtable的一部分实现,int和float不具有vtable。 – jmucchiello 2009-10-04 19:19:09

+3

撇开它为Comeau/G ++/MSVC编译的事实,我没有看到标准中将'typeid'限制为多态类的任何内容。 – GRB 2009-10-04 19:52:59

回答

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也不是浮点数,你会得到一个编译时错误。我希望你明白这个主意。

+0

我修改了上面的代码,问题实际上是在编译时else子句出错,它应该落入if语句的第一个分支。 – Jamie 2009-10-04 16:56:14

+0

你能否提供一个在I class内部使用的例子,上面的内容似乎是我所追求的,但我似乎无法让它工作。你也会碰巧有任何使用这种方式使用模板的参考我的c + +参考没有提到这种方法。 – Jamie 2009-10-04 17:41:40

0

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

lua_toString的回归显然是一个char*这意味着你得到的值的地址然后尝试将该地址转换为浮点数。看看strtod,看看如何更正确地做到这一点,或者正如sellibitze指出的那样,使用stringstream。

+0

感谢您的帮助,但strtod不会帮助,因为如果应该落下if语句的第一个分支。我想第二个分支conf-> getC ().....我可能应该早些时候解释过。对不起 – Jamie 2009-10-04 17:24:08

+0

@jamie - 没问题,我发布后看到你的编辑。 – 2009-10-04 19:47:30

0

我从来没有碰过lua或其引擎,但在我看来,你是在误用lua_tostring。这是它的签名:

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

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

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

因为正如我们所说的,指针可以在C/C以有意义的方式转化为integers ++,不像floats

+1

你是不是指指针指向int的“reinterpret_cast”? – sellibitze 2009-10-04 18:13:48

+0

@sellibitze没有对另一个指针类型的重新解释。这里的转换是char * - > int,它被定义,但char * - > float不允许。这是简单的static_cast。 – AraK 2009-10-04 18:37:02

+0

reinterpret_cast不限于指针 - >指针转换。 (int)(p)相当于reinterpret_cast (p)其中p是一些指针。标准不允许使用static_cast (p)。 – sellibitze 2009-10-04 20:03:35

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

的专业化然后将决定什么应该使用字符串,什么号码转换“配置”。

+1

什么是C风格的演员? lua_tostring似乎返回一个“char *”或“char const *”。使用T = int或T = long - 满足sizeof(T)== sizeof(char *) - 将成为reinterpret_cast。我几乎可以肯定,这不是杰米想要的。 – sellibitze 2009-10-04 20:07:43

+0

对于'int',使用'lua_tonumber'。不会有'lua_tostring'参与:) – 2009-10-04 20:16:17

+0

是的。但仍然...我可能会使用功能风格的演员。 +1为优雅的解决方案! :) – sellibitze 2009-10-04 21:45:14

0

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

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

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

0

尽管Lua网站说了这样的话,但它在逻辑上是合理的,在测试时我发现指针在状态关闭后依然有效。虽然他是对的,如果你想在你的Lua函数返回后保留它,你应该复制这个字符串,但这可能不是他的问题。