c++ - 摆脱样板代码的想法
问题描述
我的问题非常具体。我有以下要求,我需要从父类设置子类中存在的成员变量,原因有几个。我的计划是在构造时将 setter 方法(存在于子类中)的函数指针(它以字符串作为参数)传递给父类。父类定义了一个以成员名和值字符串为参数的公共方法,并以字符串值调用成员的函数指针。父类可以存在于 dll 或 lib 中,并且无法访问任何转换或工厂方法,因此必须在子类中定义 setter 方法。
由于父类可以是其他类的基类,因此我编写了一些宏,如下所示:
#define DEFINE_VAL(Type, memberName) \
private: \
Type memberName; \
void set##memberName(std::string const& val) { \
memberName = convert_to_val(val); /* this will be a call to factory which converts string to value type*/\
/* or call to local implementation for conversion*/
}; \
#define INIT_VAL(memberName) \
{ memberName, \
[&](std::string const& val) { set##memberName(val); }}
父类和子类如下:
// parent.h probably in dll
class parent
{
public:
parent(std::map<std::string, std::function<void(std::string const&)>>& m)
: m(m)
{ }
...
private:
std::map<std::string, std::function<void(std::string const&)>> m;
};
// child.h
class child : public parent
{
public:
child() : parent({ INIT_VAL(iVal), ... })
{ }
private:
DEFINE_VAL(int, iVal);
...
};
子类可以定义许多变量,首先使用 DEFINE_VAL 宏然后使用 INIT_VAL 宏传递每个变量的 setter 方法有点烦人。这可以在一个宏中完成吗(可能在 DEFINE_VAL 中)?或者关于自动注册成员名称和函数指针到父类的任何想法?
我也将不胜感激有关完成我的要求的任何替代想法。
解决方案
由于几个原因,我需要从父类设置子类中存在的成员变量。我的计划是将setter方法(存在于子类中)的函数指针(它以字符串作为参数)传递给构造时的父类。
当调用父类构造函数时,派生类和它的成员还没有被初始化,而且学究式地,它们还不存在。因此,无法从其基类构造函数设置派生类成员。
一种解决方案是使用虚函数按名称设置成员。
在当前 C++ 中没有内置反射的情况下,要将名称与数据成员关联并生成成员访问器,最佳实践仍然是使用宏。用于此目的的最佳宏之一是BOOST_HANA_DEFINE_STRUCT
.
boost::lexical_cast<T>
可用于从转换std::string
为任何T
.
具有深度和多重继承支持的工作示例:
#include <boost/hana/define_struct.hpp>
#include <boost/hana/accessors.hpp>
#include <boost/hana/for_each.hpp>
#include <boost/hana/concat.hpp>
#include <boost/hana/length.hpp>
#include <boost/lexical_cast.hpp>
#include <unordered_map>
#include <functional>
#include <iostream>
namespace hana = boost::hana;
struct MemberSetter {
// Using void* to reduce the number of template instantiations.
using SetterFn = std::function<void(void*, std::string const&)>;
using Setters = std::unordered_map<std::string, SetterFn>;
Setters setters_;
template<class Derived, class Accessors>
MemberSetter(Derived* that, Accessors& accessors) {
hana::for_each(accessors, [this](auto const& pair) {
auto setter = [accessor = hana::second(pair)](void* vthat, std::string const& value) {
auto* that = static_cast<Derived*>(vthat);
auto& member = accessor(*that);
member = boost::lexical_cast<std::remove_reference_t<decltype(member)>>(value);
};
auto name = hana::first(pair);
setters_.emplace(std::string(hana::to<char const*>(name), hana::length(name)), std::move(setter));
});
}
bool findAndSetMember(void* that, std::string const& name, std::string const& value) const {
auto setter = setters_.find(name);
if(setter != setters_.end()) {
(setter->second)(that, value);
return true;
}
return false;
}
};
struct A {
virtual ~A() = default;
virtual bool setMember(std::string const& name, std::string const& value) = 0;
};
struct B : A {
BOOST_HANA_DEFINE_STRUCT(B,
(int, a),
(double, b)
);
bool setMember(std::string const& name, std::string const& value) override {
constexpr auto accessors = hana::accessors<B>();
static MemberSetter const setter(this, accessors);
return setter.findAndSetMember(this, name, value);
}
};
struct C : B {
BOOST_HANA_DEFINE_STRUCT(C,
(std::string, c)
);
bool setMember(std::string const& name, std::string const& value) override {
constexpr auto accessors = hana::concat(hana::accessors<B>(), hana::accessors<C>()); // Join with members of the base class.
static MemberSetter const setter(this, accessors);
return setter.findAndSetMember(this, name, value);
}
};
int main() {
C c;
c.setMember("a", "1");
c.setMember("b", "2.3");
c.setMember("c", "hello");
std::cout << c.a << ' ' << c.b << ' ' << c.c << '\n';
}
输出:
1 2.3 hello
推荐阅读
- ruby-on-rails - 选择表单内的活动管理员 has_one 重复
- javascript - 为什么这不起作用?使用 Rewire / Jest 测试非导出函数
- python - 从推文列表中删除推特用户名的最佳方法?
- swift - 如何在 SwiftUI 中绘制自定义形状?
- vb.net - 在函数中添加项目以获得总和
- sql - 在 PostgreSQL 中创建临时列
- python - Docker 映像构建中的 Python statsmodels 要求问题
- azure-cosmosdb - 如何查询 Cosmos DB 以获得来自结果集中多个项目的数组
- powershell - 创建多个广告组并设置电子邮件的脚本(不使用交换)
- powershell - 如何在 Powershell 中为多个 word 文档运行断开链接选项