2015-08-28 69 views
3

当前,我为C-Library编写了一个C++包装器,该C-Library作为相机的驱动程序。相机可以设置属性,可以是float,boolint。每个酒店都有一个身份证,该证件是enum PropId的成员。对于这些类型的,还有一个属性信息struct,一个函数来获取和设置值:将类型作为枚举类型的C-API封装在C++中

GetPropertyAttribsI 
GetPropertyAttribsB 
GetPropertyAttribsF 
PropAttribsI 
PropAttribsF 
PropAttribsB 
SetPropertyValueI 
SetPropertyValueF 
SetPropertyValueB 

我的问题是,现在我要编写代码:

  1. 检查是否要设置的属性值在范围内,否则将其设置为默认值。边界和默认值可以在PropAttribs struct中查找,可以使用相应的GetPropertyAttribs函数进行初始化。
  2. 设置属性与相应SetPropertyValue(I,B,F)

我可以看一下属性的类型,它是在enum (PROP_TYPE_INT, PROP_TYPE_FLOAT, PROP_TYPE_BOOL)

所以,我要的是一个功能

checkAndSanitizeProperty(T& value, PropId property) 

,检查一个给定的属性是否超出范围,否则将其设置为默认值。

框架将如下,但我不知道如何使它与模板参数非常通用,它可用于boolfloat而无需复制。设置参数的功能与此非常相似,并且如果找到任何参数,应该有相同的解决方案。

void CameraHandle::checkAndSanitizeProperty(int& value, VRmPropId property, std::string name) { 
    VRmPropInfo propInfo; 
    VRM_CHECK(VRmUsbCamGetPropertyInfo(device, property, &propInfo)); 

    if (VRM_PROP_TYPE_INT != propInfo.m_type) { 
    ROS_ERROR("Invalid type of property!"); 
    } 

    VRmPropAttribsI attribs; 
    VRmUsbCamGetPropertyAttribsI(device, property, &attribs); 
    if (value < attribs.m_min || value > attribs.m_max) { 
    ROS_WARN("Invalid value for parameter %s, has to be in [%d,%d], but was: %d", 
      name.c_str(), 
      attribs.m_min, 
      attribs.m_max, 
      value); 

    ROS_WARN("Default will be used for %s: %d", name.c_str(), attribs.m_default); 
    value = attribs.m_default; 
    } 
} 

对我的神经有什么让我不得不重复许多代码,所以我寻找一个更干净的解决方案。我不主要使用C++,所以我对模板魔法或C++习惯用法没有太多的经验。

回答

2

宏hackery。

#define CONCAT2(A, B) A##B 
#define CONCAT(A,B) CONCAT2(A,B) 

#define MAP_STRUCT(X) \ 
    template<class T> struct X; \ 
    template<class T> using CONCAT(X,_t) = typename X<T>::type; \ 
    template<> struct X<int> { \ 
    using type= CONCAT(X, I); \ 
    }; \ 
    template<> struct X<float> { \ 
    using type= CONCAT(X, F); \ 
    }; \ 
    template<> struct X<bool> { \ 
    using type= CONCAT(X, B); \ 
    }; 

需要一个底座结构名称等VRmPropAttribs并使得使得VRmPropAttribs_t<int>VRmPropAttribsI

#define CALL(X) CONCAT(call_, X) 
#define MAP_FUNC_CALL(X) \ 
    template<class T> \ 
    struct CALL(X); \ 
    template<> struct CALL(X)<int> { \ 
    template<class...Args> \ 
    auto operator()(Args&&...args)const \ 
    -> decltype(CONCAT(X,I)(std::declval<Args>()...)) { \ 
     return CONCAT(X,I)(std::forward<Args>(args)...); \ 
    } \ 
    }; \ 

对于B等,并F.

#define MAP_FUNC(X) \ 
    MAP_FUNC_CALL(X) \ 
    template<class T, class...Args> \ 
    auto X(Args&&...args) \ 
    -> typename std::result_of< CALL(X)(Args...) > \ 
    { return CALL(X)<T>{}(std::forward<Args>(args)...); } 

它定义了一个名为X一个函数模板,通过int时,调用XI具有相同的参数。现在

,这不处理你%d格式字符串之类的,但你可以做这样的事情:

VRmPropAttribs attribs; VRmUsbCamGetPropertyAttribs(device,property,& attribs);在使用上述宏之后使用 。一旦你可以做到上述,你可以用T替换int,并使你的方法成为模板方法。

您还需要一个更好的版本的VRM_PROP_TYPE_INT

template<class T> struct vrm_prop_type{}; 
template<> struct vrm_prop_type<int>: 
    std::integral_constant<int, VRM_PROP_TYPE_INT> 
{}; 

等让你做:

vrm_prop_type<T>{} != propInfo.m_type 

检查。

+0

这看起来很酷。不幸的是,我认为它比简单地复制每种类型的函数的可读性和可维护性都差。 – reindeer

+0

@reindeer如果您只有1或2个函数,那么编写手工生成的宏比使用上述宏更好。最后你会得到一个统一的商业逻辑功能,这很好。 – Yakk