优美的代码(函数/对象索引管理)

c++多态在本文被作者片面的解释为根据运行条件不同,选择不同的对象,执行同名但实现有差别的函数。如下代码所示。

class base_algorithm {
public:
	base_algorithm() = default;
	~base_algorithm() = default;
public:
	virtual const char* signature() {
		return typeid(*this).name();
	}
	virtual void apply() {
		std::cout << "ur own base algorithm implementation..." << std::endl;
	}
};

class derived_algorithm {
public:
	derived_algorithm() = default;
	~derived_algorithm() = default;
public:
	virtual const char* signature() {
		return typeid(*this).name();
	}
    
	virtual void apply() {
		std::cout << "ur own derived algorithm implementation..." << std::endl;
	}
};

关于基类base_algorithm的具体实现,你仍然可以把其中的applysignature函数设置为纯虚函数。一般情况下,我会使用如std::map<std::string, base_algorithm*>(为了方便此处写为普通指针,项目使用中也可使用智能指针std::shared_ptr<base_algorithm>)管理基类与继承类时候往往可以带来很多方便,有一点潜在的问题是,由于平台差异,typeid(*this).name()可能会返回不同的信息,在msvc编译器返回的就是class base_algorithm,这就需要根据不同的平台进行不同的parse。如果将signature函数的实现换为

virtual size_t signature() {
	return typeid(*this).hash_code();
}

相应的,存储容器也要换为std::map<size_t, base_algorithm*>,这样子可以省去针对不同平台写不同的parser
如上篇博文所说,当使用这个模式对函数进行管理的时候,会有比较大的问题,当函数接口即除了函数名字以外的签名完全相同的时候,我们可以很方便的定义一个map对象,其·key·为函数名字的hash_code,对应的键值为std::function包装的函数签名。在类的signature函数中使用hash_code是比较方便的,我之前使用过一段时间的函数模板非类型参数签名方法,根据不同派生类的名字不同进行相应的编码,当然要在类的定义上加入一个非类型的模板参数,一般来说,类的名字没有太长的话,使用int足够了。

template<int _signature>
void specifical_function {
	std::cout << "specifical_function ..." << std::endl;
}

对函数签名的计算要在编译阶段完成,所以我就使用了模板元编程的计算法则,通过将char类型的数据转换成int类型。具体实现如下,现在看来这些实现确实有些笨重,低效。如果必须使用这种签名的话,现在使用泛型lambda可以轻松优美的完成这个任务。

template<char...>
struct _signature_type {
};

template<typename>
struct _signature {
	using type = std::void_t<>;
	static const int value = -1;
};

template<>
struct _signature<_signature_type<>> {
	using type = std::void_t<>;
	static const int value = 0;
};

template<char _>
struct _signature<_signature_type<_>> {
	using type = _signature_type<_>;
	static const int value = int(_);
};

template<char _1st, char _2nd>
struct _signature<_signature_type<_1st, _2nd>> {
	using type = _signature_type<_1st, _2nd>;
	static const int value = int(((int)_1st << 1) + (int)_2nd);
};

template<char _1st, char _2nd, char... _>
struct _signature<_signature_type<_1st, _2nd, _...>> {
	using type = _signature_type<_1st, _2nd, _...>;
	static const int value = _signature<_signature_type<_1st, _2nd>>::value + _signature<_signature_type<_...>>::value;
};

template<char... _>
auto signature_v = _signature<_signature_type<_...>>::value;

当定义函数的时候,可以使用如下的方式加入数字签名。

template<int = signature_v<'s','p','e','c','i','f','i','c','a','l','_','f','u','n','c','t','i','o','n'>
void specifical_function () {
	...
}

好丑啊,当时怎么想出来这个奇葩的方法了啊,不懂当时脑子怎么想的。这个只是明确了函数的单一的签名问题,但当函数的参数不完全相同的时候,就比较难处理,引用上篇博文的叙述:虽然有些操作的物理意义接近或基本相同,但却并不一定所有的函数都有同样的输入参数,当然如果差距不大的话,可以加入一些 dummy parameter,有些情况,输入参数差别太大的时候,强行加入参数会导致代码极其丑陋。我一开始的思路是将lambda函数作为一个函数包装器,将不同参数的函数包装进一个相同的lambda内。

void parameter_1(int) {
	std::cout << "void(int)" << std::endl;
}

void parameter_2(int, int) {
	std::cout << "void(int, int)" << std::endl;
}

auto lambda_1 = [&]() {
	return std::function<void(int)>(std::bind(parameter_1, std::placeholders::_1));
};

auto lambda_2 = [&]() {
	return std::function<void(int, int)>(std::bind(parameter_2, std::placeholders::_1, std::placeholders::_2));
};

虽然通过lambda的包装,可以通过void参数获取不同的std::function对象,但是由于lambda函数返回的类型不同,还是没有办法存储进一个统一的std::map中去。确实比较棘手。往下的话,我仍然想要把这些不同的lambda存储进一个容器中,想来想去只有std::tuple这个东西可以完成任务(好像还有点印象是linux中有异质链表完成了存储不同基础数据类型的,但一时又想不起具体实现方法)

auto t = make_tuple(lambda_1, lambda_2);
auto v_i = std::get<0>(t);
auto v_i_i = std::get<1>(t);
v_i()(0);
v_i_i()(0, 0);

这样子,总算是将这些个不同的函数都存储进了一个地方,最后就是将我们获取的调用不同参数的key值与我们获取std::tuplestd::function对象的int模板参数做一个映射,这里的key可能是个enum class也可能是一个std::string,甚至仅仅是一个int数值,到这里算是完成了任务。不过回头想下,废了半天劲,这个样子跟直接将不同参数的原始函数存储进std::tuple之中好像没有一丝丝先进的意味,尴尬。但是肯定有更加优美的方案完成这个任务的,容我想想,回头再更。
待续……

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值