C ++自动将类成员公开给多个API

Sna*_*rTT 5 c++ boilerplate

简短:那里有许多聪明的库,可让您出于各种目的公开类的成员,例如序列化,暴露于lua环境等。问题是,对于每个聪明的解决方案,您都必须注册一个类的东西对于每个api,都会导致大量工作重复和重复。

什么是办法,我可以注册一个类及其成员和方法ONCE有某种附加标志,并获得各种自动生成的方法

龙:可以说我有一堂课:

class C {
public:
    int i;  // Some int
    vec3 v; // Some non-POD value
    int function(int foo, char bar) // Some function
    int hidden; // Some internal stuff I don't want exposed
        }
Run Code Online (Sandbox Code Playgroud)

我想做的是能够在声明时标记各种成员和方法,以将它们自动放入各种样板中:

所以声明可能看起来像:

class C {
public:
    LUAREG JSON BINSER int i; // Marks member "i" to be exposed to the lua enviroment,
                              // JSON serialisable, binary serialisable
    JSON vec3 v; // Only exposed to JSON function

    LUAREG void someFunction() {} // Marks this method to be exposed to the lua enviroment
    }
Run Code Online (Sandbox Code Playgroud)

或者也许编写一个注册函数来完成所有声明:

void C::register_class() {
    registrator::register_class<C>("C")
        .addData("i", &i, FLAG_LUA | FLAG_JSON | FLAG_BINARY_SERIALISABLE)
        .addFunction("someFunction", &someFunction, FLAG_LUA)
    .end_class()
    }
Run Code Online (Sandbox Code Playgroud)

(我已经看过这种模式了-它有名字吗?)我希望能够为所述类自动生成各种样板函数。例如:

/// Adds all fields with FLAG_JSON to a "json" object to be later serialised
void toJson(const C & c, json & j) {
    toJson(c.i, "i", j);
    toJson(c.v, "v", j);
    }

/// Binary seralises all members with flag FLAG_BINARY_SERIALISABLE and stores the result in s
void serialise(const C & c, string & s) {
    serialise(c.i, s);
    serialise(c.v, s);
    }

/// Registers all stuff with flag FLAG_LUA to some lua environment
void register_lua(lua_State * const L) {
        luaReg_FetchContext(L)::addClass("C")
            .addData("x", &C::x).comment("Some int") // generates documentation of "int x : Some int"
            .addData("v", &C::v).comment("Some vector")
            .addFunction("function", &C::function).comment("some function") // generates: "int function(int, char) : some function"
        .endClass()
        }

/// Register to some *other* lua environment (such as a read only context for rendering)
void register_lua_readonly(lua_State * const L2) {
        luaReg_FetchContext(L)::addClass("C")
            .addData("x", &C::x, false).comment("Some int")
        .endClass()
        }

/// Generates some user readable breakdown of all members
string dump() {
    ...
    }
Run Code Online (Sandbox Code Playgroud)

现在您可以看到许多类,这将变得非常乏味,肿胀并且易于手动出错

解决此问题的最佳方法是什么?我很想使用宏来吐出这些功能,但不知道如何进入注册阶段。干杯!

Sna*_*rTT 0

我有一个解决方案 - 我推出了自己的反射库 - https://github.com/SnapperTT/STTR

class C {
public:
    int i;
    double d;
    int func(int z) const {
        return z;
        }

    static int static_var;
    static int static_func() { return 5; }
    };
int C::static_var = 7;

struct D : public C {
    int di;
    };

int main(int argc, char ** argv) {
    // Registration
    sttr::RegNamespace mNamespace("testns");
    mNamespace.beginClass<C>("C")
        .regField(&C::i, "i")
        .regField(&C::d, "d")
            .setUserFlags(0x00F00BAA) // Sets the user flags for the last added item
            .setUserString("You can use this for whatever")
            .setUserData((void *) &someStruct)
        .regField(&C::func, "func")

        .regField(&C::static_var, "static_var")
        .regField(&C::static_func, "static_func")
    .endClass()
    .findClass("C").beginClass<D>("D") // Class inherritance. Also you can just beginClass<D>("D") inside C
        .regField(&D::di, "di")
    .endClass();
}
Run Code Online (Sandbox Code Playgroud)

这个解决方案的关键是我可以将标志附加到注册成员(因此 LUA 可以是1 << 0,JSON 可以是1 << 1,等等。

自动 json 序列化和 lua 注册是通过访问者完成的。访问者检查成员是否具有相关标志,然后执行其操作或跳到下一个成员。

我还尝试使用 RTTR: https: //github.com/rttrorg/rttr - 它是一个不错的库,尽管体积庞大。我推出了自己的库,因为我不喜欢访问者界面和大量的模板错误消息。

最后,通过运行一个脚本来完成自动文档,该脚本执行一系列正则表达式查找和替换来挑选案例typename Foo; /// Some documentation并创建static const char * const docv_Foo = "Some documentation";并注册它。这确实为编译添加了一个中间步骤,但它一点也不坏。