版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ssss1223ss/article/details/79247552
引言
我们经常在golang代码中看到这样一种惯用法:根据选项option或者配置config创建一个对象,而创建对象的工厂函数CreateXXX的输入参数不是一个大的选项结构体Options,而是Option的变长参数。举个例子:
func main() {
p := CreatePersion(WithID(123), WithName("tom"))
log.Println(p)
}
func WithID(id int) Option {
return func(opts *Options) {
opts.ID = id
}
}
func WithName(name string) Option {
return func(opts *Options) {
opts.Name = name
}
}
type Option func(*Options)
type Options struct {
ID int
Name string
}
func CreatePersion(opts ...Option) *Person {
var options Options
for _, opt := range opts {
opt(&options)
}
return &Person{
ID: options.ID,
Name: options.Name,
}
}
type Person struct {
ID int
Name string
}
func (p *Person) String() string {
return fmt.Sprintf("%d %s", p.ID, p.Name)
}
Option
是个函数类型,可以非常方便拓展定制自己的Option函数WithXXX,而不仅仅只是简单对Options成员赋值。
这种惯用法我们暂且称之为option模式。C++里面应该怎么实现options模式呢?
实现原理
golang版本options模式主要在2点:1. Option定义是个函数,支持闭包。2. CreateXXX函数参数支持变长参数。
这2点在c++11中均有支持,使用std::function、lambda表达式、变长模板参数即可完美解决。
实现代码
#include <functional>
#include <string>
#include <iostream>
struct Options {
int id;
std::string name;
};
struct Person {
int id;
std::string name;
};
std::ostream& operator<< (std::ostream& out, const Person& p) {
return out << p.id << '\t' << p.name;
}
template<typename ... Opt>
Person CreatePerson(Opt&& ... opts) {
Options options;
// fold expression since c++17
(std::forward<Opt>(opts)(options), ...);
return { options.id, options.name };
}
using Option = std::function<void(Options&)>;
Option WithID(int id) {
return [id](Options& options) {
options.id = id;
};
}
Option WithName(const std::string& name) {
return [name](Options& options) {
options.name = name;
};
}
int main() {
auto p = CreatePerson(WithID(123), WithName("tom"));
std::clog << p << std::endl;
}