我正在寻找最常见且最健壮的方法来检查void*
是否可以在给定的C ++对象类型中进行转换。您可以在下面看到有关上下文的一些信息。
当我为DLL定义一个C API时,我经常使用void *来隐藏我后面使用的C ++对象(如下所示)
typedef void* Instance_t;
int createInstance(Instance_t* pInst);
int processing(Instance_t inst, uint8_t* pBuf, size_t bufLength);
当我createInstance
代码cast
这样的指针时:
int createInstance(Instance_t* pInst)
{
MyClass* ptr = new MyClass();
*pInst = (void*)(ptr);
//.... etc
return 0;
}
但问题是我们如何在以后定义的所有其他C函数中检查我们收到的void*
值是否为有效 MyClass*
。我认为我们不能,因为C ++转换操作符都不是真正的类型安全(即使dynamic_cast
)。
现在我的最佳解决方案是使用C cast(或reinterpret_cast
),如果一切正常,调用IsValid
函数定义MyClass。
您有更好的方法进行检查吗?
答案 0 :(得分:7)
你不能这样做,除非你(比如说)从内存池中分配所有的MyClass实例,并检查你传递的地址是指向该内存池的指针。或者维护一个有效实例列表。
但是,如果你需要传递一个不透明的指针,只需让客户端使用
struct MyClass;
typedef struct MyClass *Instance_t;
这将干净利落地编译,给您一定的安心。只要您只使用指针,编译器就会很高兴。它解引它时,编译器需要知道指针实际指向的是什么。
答案 1 :(得分:5)
我认为你不能这样做,我认为你不应该这样做。 void *
只是指向内存中某个位置的指针。几乎按照定义,没有办法知道它指向的是什么。
但是为什么要将所有内容转换为void *
,为什么不在课堂上使用受保护和私有方法,如果你想阻止用户摆弄你班级的内部?
答案 2 :(得分:3)
无法检查无类型指针是否指向任何特定类型的有效对象。如果您使用void*
,则会丢弃类型检查。相反,您可以在C标头中声明类型,并使用指向该(不完整)类型的指针而不是void*
。
struct Instance;
int createInstance(struct Instance** pInst);
int processing(struct Instance* inst, uint8_t* pBuf, size_t bufLength);
然后在C ++中,您可以定义和使用该类。
// Probably better to use "struct" rather than "class" in case
// some compilers complain about mixing class keys.
struct Instance {
// Whatever
};
int createInstance(Instance** pInst) {
*pInst = new Instance;
// and so on
}
答案 3 :(得分:1)
无法确定void*
是否指向有效的C ++类。除非你已经启用了RTTI,否则没有与类关联的元数据,即使这样,在C ++中有很多情况,其中void*
没有指向类。例如:
int x=10;
void *ptr = &x;
此处ptr
指向原始值。没有RTTI与整数相关联,因此您如何查询它以确定任何>
答案 4 :(得分:0)
当我需要将CPP lib中的某些对象导出到C代码时,我会这样做:
typedef void * OBJ1;
typedef void * OBJ2;
OBJ1 createObj1();
OBJ2 createObj2();
void doObj1(OBJ1 obj);
所以在do函数中我完全知道期望什么对象
答案 5 :(得分:0)
简短的回答:这可能很难。
问题很多,但基本上归结为C和C ++中可访问的操作的低级性质(注意:可访问,但不一定合法)。 void*
的作用是任何指针都可以被强制转换,但是如果你使用其他类型,那么滥用reinterpret_cast
仍然会导致麻烦。
一个简单的解决方案是使用标记。本质上,在指针中放置一个类型id,以便您始终可以知道对象的原始类型。它很繁琐(因为每种类型都需要修改),但其他方面也很容易部署。
typedef enum {
SP_ATag,
SP_BTag,
SP_CTag,
...
} SP_Tag_t;
// External Tag
typedef struct {
SP_Tag_t tag;
void* p;
} SP_Any_t;
// Internal Tag
struct A {
SP_Tag_t tag;
...;
};
...
typedef union {
A* a;
B* b;
C* c;
} SP_Any_t;
然后,您使用void*
。
SP_Any_t
优点:
缺点:
一个更复杂的解决方案,可以是一个很好的调试帮助,是引入每类型注册表。缺点是您需要检测现有类型才能正常工作,这仍然很容易,并且它涉及更多运行时开销。但是嘿:it works!
template <typename> class Registrable;
//
// class Registry
//
class Registry {
public:
template <typename> friend class Registrable;
template <typename T>
static T* Cast(void*);
private:
struct TagType {};
using Key = std::pair<TagType const*, void*>;
using Store = std::set<Key>;
template <typename T>
static void Register(Registrable<T>* t);
template <typename T>
static void Unregister(Registrable<T>* t);
static Store& Get();
}; // class Registry
template <typename T>
T* Registry::Cast(void* const pointer) {
TagType const* const tag = &Registrable<T>::Tag;
if (Get().count(std::make_pair(tag, pointer)) == 0) { return nullptr; }
return static_cast<T*>(reinterpret_cast<Registrable<T>*>(pointer));
}
template <typename T>
void Registry::Register(Registrable<T>* t) {
TagType const* const tag = &T::Tag;
void* const pointer = reinterpret_cast<void*>(t);
Get().insert(std::make_pair(tag, pointer));
}
template <typename T>
void Registry::Unregister(Registrable<T>* t) {
TagType const* const tag = &T::Tag;
void* const pointer = reinterpret_cast<void*>(t);
Get().erase(std::make_pair(tag, pointer));
}
Registry::Store& Registry::Get() { static Store S; return S; }
//
// class Registrable
//
template <typename T>
class Registrable {
public:
static Registry::TagType const Tag;
Registrable();
~Registrable();
Registrable(Registrable&&) = default;
Registrable& operator=(Registrable&&) = default;
Registrable(Registrable const&) = default;
Registrable& operator=(Registrable const&) = default;
}; // class Registrable
template <typename T> Registry::TagType const Registrable<T>::Tag;
template <typename T>
Registrable<T>::Registrable() { Registry::Register(this); }
template <typename T>
Registrable<T>::~Registrable() { try { Registry::Register(this); } catch(...) {} }
答案 6 :(得分:0)
不要使用指针而是处理
这适用于单个dll。当然,如果来自1.dll的指针通过应用程序传递给2.dll,它就不起作用。在这种情况下,无论如何你都在尽可能冰上。
答案 7 :(得分:0)
我有一个使用RTTI的一些限制的解决方案......
如果您的实例都来自虚拟基类,那么您可以安全地重新解释转换为该基类,然后动态转换为您的其他类......
class Object
{
virtual ~Object() {}
};
class A : public Object
{
static bool IsOfThisClass(void *data)
{
return dynamic_cast<A*>((Object*)data) != 0;
}
}
如果someData属于A类,则调用A :: IsOfThisClass(someData)将返回true。
它不是你想要向用户公开的那种黑客,因为它只有在void *指向从Object派生的类时才有效,但它在受控情况下可能是一个有用的构建块。