为C++;消费 在C++应用程序中,我必须与C库进行接口。这个库有几个组件,每个组件都有自己的API。这些API非常相似,只是在函数的前缀和某些处理程序类型上有所不同。例如: // bull component void bull_create(bull_handle_t** handle, error_details_t* error_details); void bull_destroy(bull_handle_t* handle, error_details_t* error_details); void bull_get_data(bull_handle_t* handle, uint8_t* buffer, error_details_t* error_details); // frog component void frog_create(frog_handle_t** handle, error_details_t* error_details); void frog_destroy(frog_handle_t* handle, error_details_t* error_details); void frog_get_data(frog_handle_t* handle, uint8_t* buffer, error_details_t* error_details); // bullfrog component void bullfrog_create(bullfrog_handle_t** handle, error_details_t* error_details); void bullfrog_destroy(bullfrog_handle_t* handle, error_details_t* error_details); void bullfrog_get_data(bullfrog_handle_t* handle, uint8_t* buffer, error_details_t* error_details); // NOTE: components can and will be added at any time following the same pattern. 我想用一个通用的方式将这个打包成C++消费,代码重复最少。 因此,首先是天真的方法: class bull_backend { public: ... void get_data (uint8_t* buffer) { error_details_t err; bull_get_data(handle, buffer, &err); } ... private: bull_handle_t* handle; }; class frog_backend { public: ... void get_data (uint8_t* buffer) { error_details_t err; frog_get_data(handle, buffer, &err); } ... private: frog_handle_t* handle; }; class bullfrog_backend { public: ... void get_data (uint8_t* buffer) { error_details_t err; bullfrog_get_data(handle, buffer, &err); } ... private: bullfrog_handle_t* handle; };

为C++;消费 在C++应用程序中,我必须与C库进行接口。这个库有几个组件,每个组件都有自己的API。这些API非常相似,只是在函数的前缀和某些处理程序类型上有所不同。例如: // bull component void bull_create(bull_handle_t** handle, error_details_t* error_details); void bull_destroy(bull_handle_t* handle, error_details_t* error_details); void bull_get_data(bull_handle_t* handle, uint8_t* buffer, error_details_t* error_details); // frog component void frog_create(frog_handle_t** handle, error_details_t* error_details); void frog_destroy(frog_handle_t* handle, error_details_t* error_details); void frog_get_data(frog_handle_t* handle, uint8_t* buffer, error_details_t* error_details); // bullfrog component void bullfrog_create(bullfrog_handle_t** handle, error_details_t* error_details); void bullfrog_destroy(bullfrog_handle_t* handle, error_details_t* error_details); void bullfrog_get_data(bullfrog_handle_t* handle, uint8_t* buffer, error_details_t* error_details); // NOTE: components can and will be added at any time following the same pattern. 我想用一个通用的方式将这个打包成C++消费,代码重复最少。 因此,首先是天真的方法: class bull_backend { public: ... void get_data (uint8_t* buffer) { error_details_t err; bull_get_data(handle, buffer, &err); } ... private: bull_handle_t* handle; }; class frog_backend { public: ... void get_data (uint8_t* buffer) { error_details_t err; frog_get_data(handle, buffer, &err); } ... private: frog_handle_t* handle; }; class bullfrog_backend { public: ... void get_data (uint8_t* buffer) { error_details_t err; bullfrog_get_data(handle, buffer, &err); } ... private: bullfrog_handle_t* handle; };,c++,c,c++11,design-patterns,C++,C,C++11,Design Patterns,这并不能完全解决问题。我将只是复制C API,但现在是以类的形式。 唯一不同的是识别C实体的前缀 我脑子里的下一件事是,做好准备,一个宏 #define GENERATE_BACKEND(...) ... 这将取代前缀。但我不喜欢这样。感觉不像 现代C++ 我可以做的另一件事是将所有内容分组到一个类模板中,然后 如果,例如 template <typename Component> class backend { public: ... template&

这并不能完全解决问题。我将只是复制C API,但现在是以类的形式。 唯一不同的是识别C实体的前缀

我脑子里的下一件事是,做好准备,一个宏

#define GENERATE_BACKEND(...)
    ...
这将取代前缀。但我不喜欢这样。感觉不像 现代C++ 我可以做的另一件事是将所有内容分组到一个类模板中,然后 如果,例如

template <typename Component>
class backend
{
public:

    ...
    template<typename = typename std::enable_if<std::is_same<Component, bull_component>::value>::type>
    void get_data (uint8_t* buffer)
    {
       error_details_t err;
       bull_get_data(handle, buffer, &err);
    }

    template<typename = typename std::enable_if<std::is_same<Component, frog_component>::value>::type>
    void get_data (uint8_t* buffer)
    {
       error_details_t err;
       frog_get_data(handle, buffer, &err);
    }

    template<typename = typename std::enable_if<std::is_same<Component, bullfrog_component>::value>::type>
    void get_data (uint8_t* buffer)
    {
       error_details_t err;
       bullfrog_get_data(handle, buffer, &err);
    }
    ...

private:
    typename Component::handle_type handle;
};

struct frog_component
{
   using handle_type = frog_handle_t*;
};

struct bull_component
{
   using handle_type = bull_handle_t*;
};

struct bullfrog_component
{
   using handle_type = bullfrog_handle_t*;
};

using frog_backend = backend<frog_component>;
using bull_backend = backend<bull_component>;
using bullfrog_backend = backend<bullfrog_component>;
模板
类后端
{
公众:
...
模板
无效获取数据(uint8\u t*缓冲区)
{
错误详细信息错误;
bull_get_数据(句柄、缓冲区和错误);
}
模板
无效获取数据(uint8\u t*缓冲区)
{
错误详细信息错误;
frog_获取_数据(句柄、缓冲区和错误);
}
模板
无效获取数据(uint8\u t*缓冲区)
{
错误详细信息错误;
牛蛙获取数据(句柄、缓冲区和错误);
}
...
私人:
typename组件::句柄\类型句柄;
};
结构frog_组件
{
使用手柄类型=蛙式手柄t*;
};
结构bull_组件
{
使用句柄类型=布尔句柄类型*;
};
结构牛蛙组件
{
使用手柄类型=牛蛙手柄t*;
};
使用frog_backend=后端;
使用bull_backend=backend;
使用牛蛙_后端=后端;
但感觉还是不对。除了明显的代码之外,还有一件事困扰着我 重复的是,除了宏版本:),它们不能很好地伸缩 C组分的添加

我总觉得一定还有更好的东西。 那么,有人知道现代C++的一个更好的、有价值的技术吗?
这种情况?

我认为@Jarod42是在暗示什么

template <typename T>
class component
{
private:
    typedef void (*destroyMethod)(T*,error_details_t*);
    ...
public:
    component(destroyMethod,...)
    {
        destroy = destroyMethod;
        ...
    }
    void Destroy()
    {
        error_details_t err;
        destroy(handle, err);
    }
    ...
private:
    destroyMethod destroy;
    T * handle;
};
模板
类组件
{
私人:
类型定义无效(*销毁方法)(T*,错误详细信息T*);
...
公众:
组件(方法,…)
{
销毁=销毁方法;
...
}
无效销毁()
{
错误详细信息错误;
破坏(处理、错误);
}
...
私人:
销毁方法销毁;
T*手柄;
};
然后用类似以下内容进行实例化:

component<bull_handle_t> yourBull(bull_destroy, ...)
组件yourBull(bull\u destroy,…)

你可以用一些额外的工作来结束最后一部分

我想@Jarod42是在暗示什么

template <typename T>
class component
{
private:
    typedef void (*destroyMethod)(T*,error_details_t*);
    ...
public:
    component(destroyMethod,...)
    {
        destroy = destroyMethod;
        ...
    }
    void Destroy()
    {
        error_details_t err;
        destroy(handle, err);
    }
    ...
private:
    destroyMethod destroy;
    T * handle;
};
模板
类组件
{
私人:
类型定义无效(*销毁方法)(T*,错误详细信息T*);
...
公众:
组件(方法,…)
{
销毁=销毁方法;
...
}
无效销毁()
{
错误详细信息错误;
破坏(处理、错误);
}
...
私人:
销毁方法销毁;
T*手柄;
};
然后用类似以下内容进行实例化:

component<bull_handle_t> yourBull(bull_destroy, ...)
组件yourBull(bull\u destroy,…)

您可以通过一些额外的工作来结束最后一部分

您有一组C函数,它们的名称和签名遵循一致的模式。如果你想C++包装器调用这些函数中的一个,函数的名字必须在C++代码中出现。要实现这一点,您可以选择将名称直接插入包装器代码中的适当位置,或者通过宏系统地生成它们。你已经拒绝了这两种选择


如果你坚持避免宏,你可以考虑编写代码生成器来输出所有类定义的C++源(从字面上调用所需的函数)。也许这比手工编写更令人满意,而且如果发现类有缺陷,如果C库更改或添加函数,或者出于任何其他原因,它将允许您轻松地重新生成类。

您有一组C函数,它们的名称和签名遵循一致的模式。如果你想C++包装器调用这些函数中的一个,函数的名字必须在C++代码中出现。要实现这一点,您可以选择将名称直接插入包装器代码中的适当位置,或者通过宏系统地生成它们。你已经拒绝了这两种选择

如果你坚持避免宏,你可以考虑编写代码生成器来输出所有类定义的C++源(从字面上调用所需的函数)。也许这比手工编写更令人满意,而且如果发现类有缺陷,如果C库更改或添加函数,或者出于任何其他原因,它将允许您轻松地重新生成类。

我想到的是

template <typename H> using create_t = void (*)(H**, error_details_t*);
template <typename H> using destroy_t = void (*)(H*, error_details_t*);
template <typename H> using get_data_t = void (*)(H*, uint8_t* buffer, error_details_t*);

template <typename H,
          create_t<H> create,
          destroy_t<H> destroy,
          get_data_t<H> get_data>
class backend
{
    backend() { error_details_t err; create(&handle, err);}
    ~backend() {  error_details_t err; destroy(handle, err);}

    backend(const backend&) = delete;
    backend& operator =(const backend&) = delete;
    backend(backend&&) = delete;
    backend& operator =(backend&&) = delete;

    void get_data(uint8_t* buffer)
    {
         error_details_t err;
         get_data(handle, buffer, &err);
    }

private:
   H* handle = nullptr;
};

using bull = backend<bull_handle_t, &bull_create, &bull_destroy, &bull_get_data>;
using frog = backend<frog_handle_t, &frog_create, &frog_destroy, &frog_get_data>;
using bullfrog = backend<bullfrog_handle_t,
                         &bullfrog_create,
                         &bullfrog_destroy,
                         &bull_get_data>;
使用create\u t=void(*)(H**,error\u details\u t*)创建模板;
使用destroy_t=void(*)(H*,错误详细信息_t*)的模板;
使用get_data_t=void(*)(H*,uint8_t*缓冲区,错误详细信息_t*)的模板;
模板
类后端
{
backend(){error\u details\u t err;create(&handle,err);}
~backend(){error\u details\u t err;destroy(handle,err);}
后端(const-backend&)=删除;
后端和运算符=(const-backend&)=删除;
后端(后端&&)=删除;
后端和运算符=(后端和运算符)=删除;
无效获取数据(uint8\u t*缓冲区)
{
错误详细信息错误;
获取_数据(句柄、缓冲区和错误);
}
私人:
H*handle=nullptr;
};
使用bull=backend;
使用frog=backend;
使用牛蛙=后端;
可能
error\u details\t
可以成为成员, 您还应该处理错误。

我想的是

template <typename H> using create_t = void (*)(H**, error_details_t*);
template <typename H> using destroy_t = void (*)(H*, error_details_t*);
template <typename H> using get_data_t = void (*)(H*, uint8_t* buffer, error_details_t*);

template <typename H,
          create_t<H> create,
          destroy_t<H> destroy,
          get_data_t<H> get_data>
class backend
{
    backend() { error_details_t err; create(&handle, err);}
    ~backend() {  error_details_t err; destroy(handle, err);}

    backend(const backend&) = delete;
    backend& operator =(const backend&) = delete;
    backend(backend&&) = delete;
    backend& operator =(backend&&) = delete;

    void get_data(uint8_t* buffer)
    {
         error_details_t err;
         get_data(handle, buffer, &err);
    }

private:
   H* handle = nullptr;
};

using bull = backend<bull_handle_t, &bull_create, &bull_destroy, &bull_get_data>;
using frog = backend<frog_handle_t, &frog_create, &frog_destroy, &frog_get_data>;
using bullfrog = backend<bullfrog_handle_t,
                         &bullfrog_create,
                         &bullfrog_destroy,
                         &bull_get_data>;
使用create\u t=void(*)创建模板(H**,错误