首页 > 解决方案 > 摆脱样板代码的想法

问题描述

我的问题非常具体。我有以下要求,我需要从父类设置子类中存在的成员变量,原因有几个。我的计划是在构造时将 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 中)?或者关于自动注册成员名称和函数指针到父类的任何想法?

我也将不胜感激有关完成我的要求的任何替代想法。

标签: c++c++11reflectionmacros

解决方案


由于几个原因,我需要从父类设置子类中存在的成员变量。我的计划是将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

推荐阅读