属性系统基于std::any:模板类型推导

Property system based on std::any: template type deduction

为了实现多态对象的属性系统,我首先声明了以下结构:

enum class access_rights_t
{
    NONE        = 0,
    READ        = 1 << 0,
    WRITE       = 1 << 1,
    READ_WRITE  = READ | WRITE
};

struct property_format
{
    type_index type;

    string name;

    access_rights_t access_rights;
};

因此 属性 定义了类型、名称和访问权限(只读、只写或读写)。然后我开始 属性 class 如下:

template<typename Base>
class property : property_format
{

public:

    template<typename Derived, typename T>
    using get_t = function<T(const Derived&)>;

    template<typename Derived, typename T>
    using set_t = function<void(Derived&, const T&)>;

private:

    get_t<Base, any> get_f;
    set_t<Base, any> set_f;

属性 与基类型相关联,但可能(并且将会)填充与派生类型的实例相关联的访问器。访问器将封装在 Base 类型的实例上访问 std::any 对象的函数。 getset 方法声明如下(此处未显示类型检查以使代码最少):

public:

    template<typename T>
    T get(const Base& b) const
    {
        return any_cast<T>(this->get_f(b));
    }

    template<typename T>
    void set(Base& b, const T& value_)
    {
        this->set_f(b, any(value_));
    }

然后是构造函数(访问权限设置为 NONE 以使代码最少):

    template<typename Derived, typename T>
    property(
        const string& name_,
        get_t<Derived, T> get_,
        set_t<Derived, T> set_ = nullptr
    ):
        property_format{
            typeid(T),
            name_,
            access_rights_t::NONE
        },
        get_f{caller<Derived, T>{get_}},
        set_f{caller<Derived, T>{set_}}
    {
    }

    template<typename Derived, typename T>
    property(
        const string& name_,
        set_t<Derived, T> set_
    ):
        property{
            name_,
            nullptr,
            set_
        }
    {
    }

作为参数传递的函数通过辅助结构封装caller:

private:

    template<typename Derived, typename T>
    struct caller
    {
        get_t<Derived, T> get_f;
        set_t<Derived, T> set_f;

        caller(get_t<Derived, T> get_):
            get_f{get_}
        {
        }

        caller(set_t<Derived, T> set_):
            set_f{set_}
        {
        }

        any operator()(const Base& object_)
        {
            return any{
                this->get_f(
                    static_cast<const Derived&>(object_)
                )
            };
        }

        void operator()(Base& object_, const any& value_)
        {
            this->set_f(
                static_cast<Derived&>(object_),
                any_cast<Value>(value_)
            );
        }
    };

现在,考虑这些虚拟 classes。

struct foo
{
};

struct bar : foo
{
    int i, j;

    bar(int i_, int j_):
        i{i_},
        j{j_}
    {
    }

    int get_i() const {return i;}

    void set_i(const int& i_) { this->i = i_; }
};

我可以写出如下代码:

int main()
{
    // declare accessors through bar methods

    property<foo>::get_t<bar, int> get_i = &bar::get_i;
    property<foo>::set_t<bar, int> set_i = &bar::set_i;

    // declare a read-write property

    property<foo> p_i{"bar_i", get_i, set_i};

    // declare a getter through a lambda

    property<foo>::get_t<bar, int> get_j = [](const bar& b_){ return b_.j; };

    // declare a read-only property

    property<foo> p_j{"bar_j", get_j};

    // dummy usage

    bar b{42, 24};
    foo& f = b;

    cout << p_i.get<int>(f) << " " << p_j.get<int>(f) << endl;
    p_i.set<int>(f, 43);
    cout << p_i.get<int>(f) << endl;
}

我的问题是模板类型推导不允许我声明一个 属性 直接将访问器作为参数传递,如:

property<foo> p_i{"bar_i", &bar::get_i, &bar::set_i};

这会产生以下错误:

prog.cc:62:5: note: template argument deduction/substitution failed:

prog.cc:149:50: note: mismatched types std::function<void(Type&, const Value&)> and int (bar::*)() const

property<foo> p_i{"bar_i", &bar::get_i, set_i};

有没有办法在保留代码的同时解决这个问题"simple"?

完整的实例可用here

在这个答案的最后是一个略有不同的方法。不过,我将从一般问题开始。

问题是 &bar::get_i 是一个指向成员函数的函数指针,而您的别名正在创建一个需要 class 作为附加模板参数的函数对象。 一些例子:

非成员函数:

#include <functional>

void a(int i) {};

void f(std::function<void(int)> func)
{
}

int main()
{
  f(&a);
  return 0;
}

这很好用。现在,如果我将 a 更改为结构:

#include <functional>

struct A
{
void a(int i) {};
};

void f(std::function<void(int)> func)
{
}


int main()
{
  f(std::function<void(int)>(&A::a));
  return 0;
}

这得到错误:

error: no matching function for call to std::function<void(int)>::function(void (A::*)(int))'

因为 std::function 对象还需要基础 class(就像您对别名声明所做的那样)

你需要一个std::function<void(A,int)>

虽然你不能让你的例子变得更好。

一种使它 "bit" 比您的示例更容易的方法可能是使用 CRTP 的这种方法。

#include <functional>

template <typename Class>
struct funcPtr
{
  template <typename type>
  using fun = std::function<void(Class,type)>;
};

struct A : public funcPtr<A>
{
void a(int i) {};
};

void f(A::fun<int> func)
{
};


int main()
{
  f(A::fun<int>(&A::a));
  return 0;
}

并且您的每个 "derived" classes 都派生自 funcPtr class,其中 "auto generates" 特定的别名声明。

std::function是类型擦除类型。类型擦除类型不适合推导。

template<typename Derived, typename T>
using get_t = function<T(const Derived&)>;

get_t 是类型擦除类型的别名。同上。

创建特征 classes:

template<class T>
struct gettor_traits : std::false_type {};

这会告诉您 T 是否是一个有效的 gettor,如果是,它的输入和输出类型是什么。 settor_traits.

同样

所以

 template<class T, class Derived>
 struct gettor_traits< std::function<T(Derived const&)> >:
   std::true_type
 {
   using return_type = T;
   using argument_type = Derived;
 }; 
 template<class T, class Derived>
 struct gettor_traits< T(Derived::*)() >:
   std::true_type
 {
   using return_type = T;
   using argument_type = Derived;
 }; 

等等

现在我们回到 property 构造函数:

template<class Gettor,
  std::enable_if_t< gettor_traits<Gettor>{}, int> =0,
  class T = typename gettor_traits<Gettor>::return_value,
  class Derived = typename gettor_traits<Gettor>::argument_type
>
property(
    const string& name_,
    Gettor get_
):
    property_format{
        typeid(T),
        name_,
        access_rights_t::NONE
    },
    get_f{caller<Derived, T>{get_}},
    nullptr
{
}

我们使用 SFINAE 来确保我们的 Gettor 通过集合,并使用特征 class 来提取我们关心的类型。

这里会有很多工作要做。但它是一次写入工作。

在这些情况下我的首选语法是:

std::cout << (f->*p_i)();

(f->*p_i)(7);

其中 属性 充当成员函数指针,甚至

(f->*p_i) = 7;
std::cout << (f->*p_i);

其中 属性 透明地充当成员变量指针。

在这两种情况下,通过 ->* 的重载,在第二种情况下,通过从 ->*.

返回伪引用