C++ 可变模版非常方便,可以实现任意数量的参数输入,下面用C++可变模版模仿python matplotlib.pyplot.plot 函数,实现任意数量参数输入,并且识别输入变量的类型
类型定义
用宏定义定义一些结构体,实现marker、linewidth等标记类型和变量
#define MO_KEYWORD_INPUT(name, type) \
namespace tag{ \
struct name { \
typedef type Type; \
typedef const Type& ConstRef; \
typedef Type& Ref; \
typedef ConstRef StorageType; \
typedef const void* VoidType; \
template <typename T> \
static PLT_CONSTEXPR bool AllowedType() { return std::is_same<Type, T>::value; } \
static VoidType GetPtr(ConstRef arg) { \
return &arg; \
} \
template <class T> \
static VoidType GetPtr(const T& arg) { \
(void)arg; \
return nullptr; \
} \
}; \
} \
namespace PLT_ARG_NAMESPACE { \
static kwargs::TKeyword<tag::name>& name = kwargs::TKeyword<tag::name>::instance; \
}
上面的PLT_ARG_NAMESPACE命名空间定义了static类型为tag::name的变量name,赋值的是instance变量,注意,instance变量是结构体TKeyword类型,其中instance成员是static类型,这意味着所有的name共享同一个TKeyword类型
namespace kwargs {
struct TaggedBase {};
template <class Tag>
struct TaggedArgument : public TaggedBase {
typedef Tag TagType;
explicit TaggedArgument(typename Tag::StorageType val) //explicit显示赋值,避免隐式赋值
: arg(&val) {
}
typename Tag::VoidType get() const {
return arg;
}
protected:
typename Tag::VoidType arg;
};
template <class Tag>
struct TKeyword {
static TKeyword instance; //初始化后所有类的instance共享同一个TKeyword类
TaggedArgument<Tag> operator=(typename Tag::StorageType data) {
return TaggedArgument<Tag>(data);
}
};
template <class T>
TKeyword<T> TKeyword<T>::instance;
}
声明
MO_KEYWORD_INPUT 定义了结构体类型marker、label等,并在PLT_ARG_NAMESPACE命名空间定义了类型为TKeyword的marker、label等变量。
MO_KEYWORD_INPUT(marker,std::string)
MO_KEYWORD_INPUT(label, std::string)
MO_KEYWORD_INPUT(color, std::string)
MO_KEYWORD_INPUT(linewidth, int8_t)
MO_KEYWORD_INPUT(alpha, float )
MO_KEYWORD_INPUT(edgecolor, std::string)
MO_KEYWORD_INPUT(markersize, double )
#define DEFAULT_LEGEND ""
#define DEFAULT_MARKER "-"
#define DEFAULT_ALPHA 1.0f
#define DEFAULT_COLOR "none"
#define DEFAULT_EDGECOLOR "none"
#define DEFAULT_LINEW 2
#define DEFAULT_MARKERSZ 6.0f
可变模版函数的展开
定义函数plotxy,输入x,y坐标,后面的参数包则为曲线的相关设置。
template <class ... Args>
void plotxy(int x,int y,const Args&... args)
{
auto marker = GetKeywordInputDefault<tag::marker>(DEFAULT_MARKER,args...);
std::cout << "maker:"<<marker << endl;
auto makersize = GetKeywordInputDefault<tag::markersize>(DEFAULT_MARKERSZ,args...);
cout << "msize:" << makersize << endl;
auto lw = GetKeywordInputDefault<tag::linewidth>(DEFAULT_LINEW,args...);
cout << "lw:" << (int)lw << endl;
}
下面的类型Tag是结构体类型,如tag::marker,GetKeywordInputDefault函数第一个参数是默认值DEFAULT_MARKER,第二个参数是参数包,调用GetKeyImpl展开参数包args
template <class Tag, bool Infer = false, class... Args>
typename Tag::ConstRef GetKeywordInputDefault(typename Tag::ConstRef def, const Args&... args) {
const void* ptr = GetKeyImpl<Tag, Infer>(args...);
if (ptr) //如果返回值不是0,表示在参数包args中知道了对应类型的参数
return *static_cast<const typename Tag::Type*>(ptr);
//args中没有找到Tag类型的参数,返回默认值
return def;
}
参数包展开的终止函数
template <class Tag, bool Infer = false>
typename Tag::VoidType GetKeyImpl() { //如果没有找到与def类型一致的类,返回0
return 0;
}
template <class Tag, bool Infer = false, class T, class... Args>
//kwargs::TaggedBase是类T的基类,则is_base_of为真,则enable_if<true,T>::type为T::type,即定义GetKeyImpl返回值为Tag::VoidType类型
typename std::enable_if<std::is_base_of<kwargs::TaggedBase, T>::value, typename Tag::VoidType>::type
GetKeyImpl(const T& arg, const Args&... args) {
//如果Tag与T::TagType类型相同,则返回arg的值,否则继续调用GetKeyImpl展开参数包
return std::is_same<typename T::TagType, Tag>::value ? const_cast<void*>(arg.get()) : const_cast<void*>(GetKeyImpl<Tag, Infer, Args...>(args...));
}
完整代码
#include <iostream>
#include "type_traits"
using namespace std;
#define PLT_CONSTEXPR constexpr
#ifndef PLT_ARG_NAMESPACE
#define PLT_ARG_NAMESPACE
#endif
#define MO_KEYWORD_INPUT(name, type) \
namespace tag{ \
struct name { \
typedef type Type; \
typedef const Type& ConstRef; \
typedef Type& Ref; \
typedef ConstRef StorageType; \
typedef const void* VoidType; \
template <typename T> \
static PLT_CONSTEXPR bool AllowedType() { return std::is_same<Type, T>::value; } \
static VoidType GetPtr(ConstRef arg) { \
return &arg; \
} \
template <class T> \
static VoidType GetPtr(const T& arg) { \
(void)arg; \
return nullptr; \
} \
}; \
} \
namespace PLT_ARG_NAMESPACE { \
static kwargs::TKeyword<tag::name>& name = kwargs::TKeyword<tag::name>::instance; \
}
namespace kwargs {
struct TaggedBase {};
template <class Tag>
struct TaggedArgument : public TaggedBase {
typedef Tag TagType;
explicit TaggedArgument(typename Tag::StorageType val) //explicit显示赋值,避免隐式赋值
: arg(&val) {
}
typename Tag::VoidType get() const {
return arg;
}
protected:
typename Tag::VoidType arg;
};
template <class Tag>
struct TKeyword {
static TKeyword instance; //初始化后所有类的instance共享同一个TKeyword类
TaggedArgument<Tag> operator=(typename Tag::StorageType data) {
return TaggedArgument<Tag>(data);
}
};
template <class T>
TKeyword<T> TKeyword<T>::instance;
}
MO_KEYWORD_INPUT(marker,std::string)
MO_KEYWORD_INPUT(label, std::string)
MO_KEYWORD_INPUT(color, std::string)
MO_KEYWORD_INPUT(linewidth, int8_t)
MO_KEYWORD_INPUT(alpha, float )
MO_KEYWORD_INPUT(edgecolor, std::string)
MO_KEYWORD_INPUT(markersize, double )
#define DEFAULT_LEGEND ""
#define DEFAULT_MARKER "-"
#define DEFAULT_ALPHA 1.0f
#define DEFAULT_COLOR "none"
#define DEFAULT_EDGECOLOR "none"
#define DEFAULT_LINEW 2
#define DEFAULT_MARKERSZ 6.0f
template <class Tag, bool Infer = false>
typename Tag::VoidType GetKeyImpl() { //如果没有找到与def类型一致的类,返回0
return 0;
}
template <class Tag, bool Infer = false, class T, class... Args>
typename std::enable_if<std::is_base_of<kwargs::TaggedBase, T>::value, typename Tag::VoidType>::type
GetKeyImpl(const T& arg, const Args&... args) {
return std::is_same<typename T::TagType, Tag>::value ? const_cast<void*>(arg.get()) : const_cast<void*>(GetKeyImpl<Tag, Infer, Args...>(args...));
}
template <class Tag, bool Infer = false, class... Args>
typename Tag::ConstRef GetKeywordInputDefault(typename Tag::ConstRef def, const Args&... args) {
const void* ptr = GetKeyImpl<Tag, Infer>(args...);
if (ptr)
return *static_cast<const typename Tag::Type*>(ptr);
return def;
}
template <class ... Args>
void plotxy(int x,int y,const Args&... args)
{
auto marker = GetKeywordInputDefault<tag::marker>(DEFAULT_MARKER,args...);
std::cout << "maker:"<<marker << endl;
auto makersize = GetKeywordInputDefault<tag::markersize>(DEFAULT_MARKERSZ,args...);
cout << "msize:" << makersize << endl;
auto lw = GetKeywordInputDefault<tag::linewidth>(DEFAULT_LINEW,args...);
cout << "lw:" << (int)lw << endl;
}
struct learn{
static learn l; //static类成员,所有l变量共享同一地址
static int count;
};
int learn::count = 100; //静态成员需要外部初始化
int main() {
// learn l1;
// l1.count = 10;
// learn l2;
// cout << "l1.cnt:" << l1.count << " " << "l2.cnt:" << l2.count << endl;
plotxy(0,0,marker="--",markersize=10.1);
return 0;
}
代码来源
参考了用Qtchart写matplotlib相似功能的代码,项目为madplotlib