Obj-C++:用于识别Objective-C类的模板元函数?

10
使用Objective-C++,我能否编写一个C++的IsObjectiveCClass<T>模板元函数,以使得IsObjectiveCClass<T>::value仅在T是Objective-C类时为true?
从C/C++语言子集的角度来看,ObjC类到底是什么?在C/C++上下文中使用MyClass*指针似乎像普通的C指针一样运作;这是否意味着MyClass也是C类型?
6个回答

6

以下是一种简单的解决方案,应该在大多数情况下都能奏效(如果不是全部的话?有谁能想到什么时候可能会失败吗?)(它使用clang 3.0通过Xcode 4.2 - 对于早期的clang版本,请使用typedefs而不是别名):

template<class T> struct IsObjectiveCClass 
{ 
  using yesT = char (&)[10];
  using noT = char (&)[1];
  static yesT choose(id);
  static noT choose(...);
  static T make();
  enum { value = sizeof(choose(make())) == sizeof(yesT) }; 

};

啊,这是我没有想到的:任何MyClass*都可以隐式转换为id。很好,谢谢。 - imre

5
您可以阅读我最近关于ObjC++的抱怨,在这个问题中。尽可能避免使用它。绝对不要尝试将Objective-C集成到C ++模板元编程中。编译器实际上可能会在空间中撕开一个洞。
开玩笑的,您正在尝试做的事情很可能是不可能的。Objective-C类只是结构体。(C ++类也只是结构体。)没有太多的编译时内省可用。 id是指向struct objc_object的C指针。在运行时,每个对象都是id,无论它的类别如何。
typedef struct objc_class *Class;
typedef struct objc_object {
    Class isa;
} *id;

1
我不会说这是不可能的 - 请看下面我的回答。而且,使用clang 3.0的Objective C++0x是一种非常酷的语言 - 尽管我承认我还没有在任何大型项目中使用过它。 - Faisal Vali
这个事实实际上使它变得容易:由于所有的 Objective-C 指针地址都是 struct objc_object 类型,其(尽管是私有的)定义习惯上必须包含一个 isa_t isa 成员 - 我个人发现 objc_object::isa 对于 SFINAE,例如 objc::traits::is_object<T> 来说已经足够好了;参见:https://github.com/fish2000/libimread/blob/master/include/libimread/objc-rt.hh#L499-L516 更多阅读:http://unixjunkie.blogspot.de/2006/02/nil-and-nil.html 以及 objc 运行时源代码 - http://www.opensource.apple.com/source/objc4/objc4-647/runtime/objc-private.h - fish2000

3
与接受的答案类似,您可以在C++17中测试类型是否可转换为id:
template <typename T>
struct is_objc_ptr : std::integral_constant<bool, 
  std::is_convertible_v<T, id> && !std::is_null_pointer_v<T>> {};
template <typename T>
constexpr bool is_objc_ptr_v = is_objc_ptr<T>::value;

测试:

static_assert(!is_objc_ptr_v<nullptr_t>);
static_assert(!is_objc_ptr_v<int>);
static_assert(!is_objc_ptr_v<char *>);
static_assert(is_objc_ptr_v<id>);
static_assert(is_objc_ptr_v<NSObject *>);

我不知道如何在编译时发现ObjC继承关系;理论上它们可以在运行时改变,所以你必须查询运行时。

1
如果您查看Xcode中C++ STL库的实现,您可以遵循其他模板特化模型的示例,例如std::is_integralstd::is_floating_point:
template <class T> struct isObjcObject     : public std::false_type { };
template <>        struct isObjcObject<id> : public std::true_type { };

其中std::false_typestd::true_type<type_traits>头文件中定义。

如果由于某种原因你的C++版本没有std::false_typestd::true_type,你可以按照以下方式自己定义:

template<bool B>   struct boolean_constant { static constexpr const bool value = B; };

template <class T> struct isObjcObject     : public boolean_constant<false> { };
template <>        struct isObjcObject<id> : public boolean_constant<true> { };

请注意,您也可以对Objective-C类执行此操作:
template <class T> struct isObjcClass        : public std::false_type { };
template <>        struct isObjcClass<Class> : public std::true_type { };

0
我会为'id'和'NSObject*'创建一个模板特化,但你总是会受到语言的限制,因为ObjC类型系统与C++类型系统是不同的。

0

与道格的答案类似,但稍微简单一些:

template<typename T>
inline constexpr bool is_objc_v = std::is_convertible_v<id,T>;

检查id是否可转换为T,而不是相反的方式,可以避免对具有用户定义隐式转换为Obj-C类型的C++类型产生误报。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接