我正在使用C ++但使用一些C API,它们通过将指针传递给结构体来工作。为了在C ++代码中使用这些结构中的数据,我正在为每个结构编写一个C ++类。例如,如果我有像
这样的C结构struct cStruct {
int32_t data;
};
然后我用以下内容包装它:
class CppWrapper {
public:
CppWrapper(cStruct* structData) : m_structData(structData) {}
// These are the interface the user will deal with
int data() const {return m_structData->data;}
void setData(int data) {m_structData->data = data;}
private:
cStruct* m_structData = nullptr;
};
可以像
一样轻松使用cStruct* s = some_c_function();
CppWrapper w(s);
w.setData(5);
但是,如果我有一个返回const cStruct*
的C函数,那么我就不能使用CppWrapper
并且必须创建一个单独的包装器,它将const cStruct*
作为构造函数参数,存储const cStruct*
并且不包含setData()
等编辑功能。
有没有办法一般编写一个能够适应const或非const指针并保持该constness的包装器?
答案 0 :(得分:0)
我最终得到的解决方案是在结构类型上模板化包装器,这样我就可以拥有一个只与const指针一起使用的模板实例它看起来像
template<typename StructType = cStruct>
class Wrapper
{
public:
Wrapper(StructType* structData) : m_structData(structData) {}
// These are the interface the user will deal with
int data() const {return m_structData->data;}
void setData(int data) {m_structData->data = data;}
private:
StructType* m_structData = nullptr;
};
我给出了模板类型的默认参数,结构类型既是一种文档形式,也是在某些常见情况下减少类型化。
只需执行
即可在代码中使用cStruct* myCArray = some_c_function();
auto wrapper = WrapperType<>(myCArray)
const cStruct* myConstCArray = some_const_c_function();
auto constWrapper = WrapperType<const cStruct>(myConstCArray);
为了进一步提供帮助,我定义了一个定义为
的make_wrapper()
函数
template <template <typename> class WrapperType, typename StructType>
WrapperType<StructType> make_wrapper(StructType* structData)
{
return WrapperType<StructType>(structData);
}
这样你就可以了
cStruct* myCArray = some_c_function();
auto wrapper = make_wrapper(myCArray)
const cStruct* myConstCArray = some_const_c_function();
auto constWrapper = make_wrapper(myConstCArray);
因此make_wrapper()
成为通用界面。
基于此,可以使用Wrapper接口
wrapper.setData(42);
std::cout << wrapper.data() << std::endl;
并致电
constWrapper.setData(42);
给出了类似assignment of member ‘cStruct::data’ in read-only object
的恰当错误。
const
为了允许Wrapper<cStruct>
转换为Wrapper<const cStruct>
(但不是相反),有必要引入一组构造函数和operator=
重载。这些构造函数只能在类的const
版本中使用。
使用std::enable_if
以及std::is_const
和std::remove_const
时,这是可行的。它不是最好的,因为它需要构造函数的伪参数,但它完成了工作。
这些声明必须放在Wrapper
的类定义中。
template <typename OtherT,
typename = typename std::enable_if<std::is_same<const OtherT, StructType>::value>::type>
Wrapper(const Wrapper<OtherT>& nonConstOther)
: m_structData(nonConstOther.m_structData) {}
template <typename Dummy = StructType,
typename std::enable_if<std::is_const<Dummy>::value>::type>
Wrapper<StructType> operator=(const Wrapper<typename std::remove_const<StructType>::type>& nonConstOther)
{ m_structData = nonConstOther.m_structData; }
friend class Wrapper<typename std::add_const<StructType>::type>;
感谢Jonathan Wakely使用std::is_same
获得更好构造函数的提示。
在C ++界面中需要不止一个以便与许多其他C结构进行交互是很常见的。因此,我认为将公共代码(特别是模板重量级的东西)分解出来以便减少样板文件是有用的。
我最终创建了一个基类observer_ptr_wrapper
(命名为类似于the proposed observer_ptr),它保留了内部:
template<typename PointerType>
class observer_ptr_wrapper
{
public:
observer_ptr_wrapper(PointerType* structData) : m_structData(structData) {}
using NonConstPointerType = typename std::remove_const<PointerType>::type;
friend class observer_ptr_wrapper<typename std::add_const<PointerType>::type>;
template <typename OtherT,
typename = typename std::enable_if<std::is_same<const OtherT, PointerType>::value>::type>
observer_ptr_wrapper(const observer_ptr_wrapper<OtherT>& nonConstOther)
: m_structData(nonConstOther.m_structData) {}
template <typename Dummy = PointerType,
typename std::enable_if<std::is_const<Dummy>::value>::type>
observer_ptr_wrapper<PointerType> operator=(const observer_ptr_wrapper<typename std::remove_const<PointerType>::type>& nonConstOther)
{m_structData = nonConstOther.m_structData;}
protected:
PointerType* m_structData = nullptr;
};
然后可以继承:
template <typename StructType = const cStruct>
class cStructWrapper : public observer_ptr_wrapper<StructType>
{
public:
// These are the interface the user will deal with
int data() const {return m_structData->data;}
void setData(int data) {m_structData->data = data;}
//This is the boilerplate
public: using observer_ptr_wrapper<StructType>::observer_ptr_wrapper;
private: using observer_ptr_wrapper<StructType>::m_structData;
};
这样整个样板文件就减少到这两行。
答案 1 :(得分:0)
你可以创建多个构造函数:
class CppWrapper {
public:
CppWrapper(cStruct* structData) : m_structData(structData), mc_structData(nullptr) {}
CppWrapper(const cStruct* structData_c) : mc_structData(structData) {}
// These are the interface the user will deal with
int data() const {return m_structData->data;}
void setData(int data) {m_structData->data = data;}
private:
cStruct* m_structData = nullptr;
const cStruct* mc_structData;
};
答案 2 :(得分:0)
从简单的事情开始怎么样?模板专业化和继承的组合对于提供您想要的东西有很长的路要走。
#include <iostream>
struct cStruct {
int32_t data;
};
template <typename T> class CppWrapper;
// const version implements const member functions (getters)
template <>
class CppWrapper<const cStruct *> {
public:
CppWrapper(const cStruct * d) : d(d) {}
int data() const { return d->data; }
private:
const cStruct * d;
};
// non-const version implements non-const member functions (setters)
template <>
class CppWrapper<cStruct *> : public CppWrapper<const cStruct *> {
public:
CppWrapper(cStruct * d) : CppWrapper<const cStruct *>(d), d(d) {}
void setData(int data) { d->data = data; }
private:
cStruct * d;
};
int main(int argc, char * argv[]) {
struct cStruct s;
s.data = 0;
CppWrapper<const cStruct *> a{&s};
CppWrapper<cStruct *> b{&s};
const CppWrapper<const cStruct *> c{&s};
const CppWrapper<cStruct *> d{&s};
// Getters...
std::cout << "a.data() = " << c.data() << std::endl;
std::cout << "b.data() = " << d.data() << std::endl;
std::cout << "c.data() = " << c.data() << std::endl;
std::cout << "d.data() = " << d.data() << std::endl;
// Setters...
std::cout << "Setting data to 1" << std::endl;
// a.setData(1); // Won't compile, as you would hope
b.setData(1);
// c.setData(1); // Won't compile, as you would hope
// d.setData(1); // Won't compile, as you would hope
// Getters (again)
std::cout << "a.data() = " << c.data() << std::endl;
std::cout << "b.data() = " << d.data() << std::endl;
std::cout << "c.data() = " << c.data() << std::endl;
std::cout << "d.data() = " << d.data() << std::endl;
return 0;
}
非const版本继承了所有const版本的'getter'函数,并使用'setter'扩展它。没有那么多的样板代码;可能还不足以保证更通用的解决方案?