在编译时检测C ++中的函数 [英] Detecting a function in C++ at compile time

查看:120
本文介绍了在编译时检测C ++中的函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有一种方法,可能使用模板,宏或两者的组合,我可以一般地应用一个函数到不同的对象类,但如果他们没有一个特定的函数,他们以不同的方式响应? p>

我特别想要应用一个函数,该函数将输出对象的大小(即集合中的对象数量),如果对象具有该函数,但会输出一个简单的替换(如N / A)如果对象不。即

  NO_OF_ELEMENTS(mySTLMap)-----> [调用mySTLMap.size()给出] ------> 10 
NO_OF_ELEMENTS(myNoSizeObj) - > [应用编译时逻辑给出] - > N / A



我希望这可能是类似于静态断言,显然要编译一个不同的代码路径而不是在构建阶段失败。

解决方案

根据我的理解,你不会一个通用测试看看一个类是否有一个特定的成员函数。这可以使用 SFINAE 在C ++中完成。在C ++ 11中,它很简单,因为你可以使用 decltype

  template< typename T> 
struct has_size {
private:
template< typename U>
static decltype(std :: declval< U>()。size(),void(),std :: true_type())test
template< typename>
static std :: false_type test(...);
public:
typedef decltype(test< T>(0))type;
enum {value = type :: value};
};

如果使用C ++ 03,由于缺少 decltype ,因此您必须滥用 sizeof

  template< typename T> 
struct has_size {
private:
struct yes {int x; };
struct no {yes x [4]; };
template< typename U>
static typename boost :: enable_if_c< sizeof(static_cast< U *>(0) - > size(),void(),int())== sizeof (int);
template< typename>
static no test(...);
public:
enum {value = sizeof(test< T>(0))== sizeof(yes)};
};

当然这使用 Boost.Enable_If 这可能是一个不需要的(和不必要的)依赖。但是写 enable_if 自己很简单:

 类型名T> enable_if; 
template< typename T> enable_if< true,T> {typedef T type; };

在这两种情况下,methodignature test< U>(int)只有可见,如果 U 有一个 size 方法,因为否则评估 decltype sizeof (取决于您使用的版本)将失败,然后会从考虑中删除该方法(由于 SFINAE 。长整型表达式 std :: declval< U>()。size(),void(),std :: true_type()是一个滥用c ++逗号运算符,它将返回逗号分隔列表中的最后一个表达式,因此这将确保类型为 std :: true_type C ++ 11变体(对于c ++ 03变体, sizeof 求值 int void()在中间只是为了确保没有奇怪的重载逗号运算符干扰评估。



当然这将返回true如果 T 有一个 size 方法可调用无参数,但不提供担保返回值。我假设你可能想检测只有那些不返回void的方法。通过稍微修改 test(int)方法就可以轻松实现这一点:

  // C ++ 11 
template< typeename U>
static typename std :: enable_if<!is_void< decltype(std :: declval< U>()。size())> :: value,std :: true_type>
// C ++ 03
template< typename U>
static typename std :: enable_if< boost :: enable_if_c< sizeof(static_cast< U *>(0) - > size())! int);


Is there a way, presumably using templates, macros or a combination of the two, that I can generically apply a function to different classes of objects but have them respond in different ways if they do not have a specific function?

I specifically want to apply a function which will output the size of the object (i.e. the number of objects in a collection) if the object has that function but will output a simple replacement (such as "N/A") if the object doesn't. I.e.

NO_OF_ELEMENTS( mySTLMap ) -----> [ calls mySTLMap.size() to give ] ------>  10
NO_OF_ELEMENTS( myNoSizeObj ) --> [ applies compile time logic to give ] -> "N/A"

I expect that this might be something similar to a static assertion although I'd clearly want to compile a different code path rather than fail at build stage.

解决方案

From what I understand you wan't to have a generic test to see if a class has a certain memberfunction. This can be accomplished in C++ using SFINAE. In C++11 it's pretty simple, since you can use decltype:

template <typename T>
struct has_size {
private:
    template <typename U>
    static decltype(std::declval<U>().size(), void(), std::true_type()) test(int);
    template <typename>
    static std::false_type test(...);
public:
    typedef decltype(test<T>(0)) type;
    enum { value = type::value };
};

If you use C++03 it is a bit harder due to the lack of decltype, so you have to abuse sizeof instead:

template <typename T>
struct has_size {
private:
    struct yes { int x; };
    struct no {yes x[4]; };
    template <typename U>
    static typename boost::enable_if_c<sizeof(static_cast<U*>(0)->size(), void(), int()) == sizeof(int), yes>::type test(int);
    template <typename>
    static no test(...);
public:
    enum { value = sizeof(test<T>(0)) == sizeof(yes) };
};

Of course this uses Boost.Enable_If, which might be an unwanted (and unnecessary) dependency. However writing enable_if yourself is dead simple:

template<bool Cond, typename T> enable_if;
template<typename T> enable_if<true, T> { typedef T type; };

In both cases the methodsignature test<U>(int) is only visible, if U has a size method, since otherwise evaluating either the decltype or the sizeof (depending which version you use) will fail, which will then remove the method from consideration (due to SFINAE. The lengthy expressions std::declval<U>().size(), void(), std::true_type() is an abuse of c++ comma operator, which will return the last expression from the comma seperated list, so this makes sure the type is known as std::true_type for the C++11 variant (and the sizeof evaluates int for the c++03 variant). The void() in the middle is only there to make sure there are no strange overloads of the comma operator interfering with the evaluation.

Of course this will return true if T has a size method which is callable without arguments, but gives no guarantees about the return value. I assume wou probably want to detect only those methods which don't return void. This can be easily accomplished with a slight modification of the test(int) method:

//C++11
template <typename U>
static typename std::enable_if<!is_void<decltype(std::declval<U>().size())>::value, std::true_type>::type test(int);
//C++03
template <typename U>
static typename std::enable_if<boost::enable_if_c<sizeof(static_cast<U*>(0)->size()) != sizeof(void()), yes>::type test(int);

这篇关于在编译时检测C ++中的函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆