在其自己的模板化成员方法中获取 class 的类型

Get type of class within its own templated member method

这有点令人困惑,但我会尽力解释。我有一组宏可以帮助我编写特殊的成员方法,这些方法在成员方法调用周围添加一个包装器。

例如:

class Test {
public:
    DECLARE_METHOD(int, methodName, (int, a) (AMoveOnlyType, b));
};

扩展为如下内容:

class Test {
public:
    int methodName(int a, AMoveOnlyType b);
    int methodName_WRAPPER(int a, AMoveOnlyType b);
};

我有一个相应的源文件宏:

//in .cpp file
DECLARE_METHOD(int, Test::methodName, (int, a) (AMoveOnlyType, b)) {
    //body of the method here
}

扩展为如下内容:

int Test::methodName(int a, AMoveOnlyType b) {
   //Assume I have a templated function which allows me to queue and call a function at some later point in time
    return execute_later(this, &Test::methodName_WRAPPER, a, b);
}
int Test::methodName_WRAPPER(int a, AMoveOnlyType b) {
    //body of the method here
}

但是,我意识到我需要能够完美地将传递给 methodName 的参数转发到我在包装代码中所做的任何事情。实现此目的的唯一方法是使 methodName 成为模板函数,这反过来意味着它需要在 header 文件中定义。所以我的 DECLARE_METHOD 宏会扩展成这样:

class Test {
public:
    template <typename ... ARGS>
    inline int methodName(ARGS&&... args) {
        return execute_later(this, &methodName_WRAPPER, std::forward<ARGS>(args)...);
    }
    int methodName_WRAPPER(int a, AMoveOnlyType b);
};

这样我就可以像这样使用它了:

Test instance;
AMoveOnlyType x;
//I can't write a macro which magically knows if the wrapper needs to move/forward the arguments so this is why I needed to use a template
instance.methodName(1, std::move(x));

问题是,我收到以下错误“ISO C++ 禁止使用绑定成员函数的地址来形成指向成员函数的指针”

我知道我需要像在 DEFINE_METHOD 宏中那样提供函数的全名。但是,由于这是在 header 文件中,我真的更愿意像以前一样使用我的 DECLARE_METHOD 宏,而不必冗余地提供 class 的名称(就像你一样在 class).

中声明普通方法时会

我的问题是,有没有办法以某种方式获取指向 header 中的 class' 成员方法的指针,而无需提供其全名?

如果我需要澄清任何事情,请告诉我。

作为旁注,我真的很想在不影响以下语句的范围,但我看不到执行此操作的方法。

编辑

根据评论中的要求,这里是宏:

/**
 * @brief This macro will remove parenthesis from around its argument only
 *        if they are present.
 *
 * See 
 */
#define DEPAREN(X) ESC(ISH X)
#define ISH(...) ISH __VA_ARGS__
#define ESC(...) ESC_(__VA_ARGS__)
#define ESC_(...) VAN ## __VA_ARGS__
#define VANISH

#define END(...) END_(__VA_ARGS__)
#define END_(...) __VA_ARGS__##_END

#define END_C(...) END_C_(__VA_ARGS__)
#define END_C_(...) __VA_ARGS__##_END_C

//------------------------------------------------------------------------------

#define PARAMS_LOOP(...) END(PARAMS_LOOP_0 __VA_ARGS__)
#define PARAMS_LOOP_C(...) END_C(PARAMS_LOOP_0_C __VA_ARGS__)
#define PARAMS_LOOP_C_POST(...) END_C(PARAMS_LOOP_0 __VA_ARGS__)
#define PARAMS_LOOP_C_PRE(...) END(PARAMS_LOOP_0_C __VA_ARGS__)

#define PARAMS_LOOP_MACRO(_1, _2, _3, NAME, ...) NAME
#define PARAMS_LOOP_BODY(...) PARAMS_LOOP_MACRO(__VA_ARGS__, PARAMS_LOOP_BODY3, PARAMS_LOOP_BODY2)(__VA_ARGS__)

#define PARAMS_LOOP_BODY2(type_, name_, ...) DEPAREN(type_) name_
#define PARAMS_LOOP_BODY3(type_, name_, default_, ...) DEPAREN(type_) name_ = default_

#define PARAMS_LOOP_0(...)     PARAMS_LOOP_BODY(__VA_ARGS__) PARAMS_LOOP_A
#define PARAMS_LOOP_0_C(...) , PARAMS_LOOP_BODY(__VA_ARGS__) PARAMS_LOOP_A
#define PARAMS_LOOP_A(...)   , PARAMS_LOOP_BODY(__VA_ARGS__) PARAMS_LOOP_B
#define PARAMS_LOOP_B(...)   , PARAMS_LOOP_BODY(__VA_ARGS__) PARAMS_LOOP_A
#define PARAMS_LOOP_0_END
#define PARAMS_LOOP_0_END_C
#define PARAMS_LOOP_0_C_END
#define PARAMS_LOOP_0_C_END_C ,
#define PARAMS_LOOP_A_END
#define PARAMS_LOOP_A_END_C ,
#define PARAMS_LOOP_B_END
#define PARAMS_LOOP_B_END_C ,

//------------------------------------------------------------------------------

/**
* Examples 1:
* NTH_LOOP(1, (int, a) (char, b) (uint8_t, c))
* END(NTH_LOOP_0_1 (int, a) (char, b) (uint8_t, c))
* NTH_LOOP_BODY_0(int, a) NTH_LOOP_A_0 (char, b) (uint8_t, c)_END
* DEPAREN(a), NTH_LOOP_BODY_0(char, b) NTH_LOOP_B_0(uint8_t, c)_END
* a, DEPAREN(b), NTH_LOOP_BODY_0(uint8_t, c) NTH_LOOP_A_1_END
* a, b, DEPAREN(c)
* a, b, c
*
* Example 2:
* NTH_LOOP(0)
* END(NTH_LOOP_0_0_END)
*
*/
#define NTH_LOOP(n_, ...)   END(NTH_LOOP_0_ ## n_ __VA_ARGS__)

/**
 * @brief Build a list containing the Nth arguments prefixed and postfixed by a
 *        comma.
 *
 * Example 1:
 * NTH_LOOP_C(0)
 * END(NTH_LOOP_0_0_C_END)
 * ,
 *
 */
#define NTH_LOOP_C(n_, ...)   END_C(NTH_LOOP_0_ ## n_ ## _C __VA_ARGS__)

/**
 * @brief Build a list containing the Nth arguments postfixed by a comma.
 *
 * **Example 1:**\n 
 * NTH_LOOP_C_POST(0)\n 
 * END(NTH_LOOP_0_0_C_END)\n 
 * ,\n 
 * \n 
 * **Example 2:**\n 
 * NTH_LOOP_C_POST(0, (int, a) (char, b))\n 
 * int, char,\n 
 */
#define NTH_LOOP_C_POST(n_, ...)   END_C(NTH_LOOP_0_ ## n_ __VA_ARGS__)

/**
 * @brief Build a list containing the Nth arguments prefixed by a comma.
 *
 * **Example 1:**\n 
 * NTH_LOOP_C_PRE(0)\n 
 * END(NTH_LOOP_0_0_C_END)\n 
 * \n 
 * \n 
 * **Example 2:**\n 
 * NTH_LOOP_C_PRE(0, (int, a) (char, b))\n 
 * , int, char\n 
 */
#define NTH_LOOP_C_PRE(n_, ...)   END(NTH_LOOP_0_ ## n_ ## _C __VA_ARGS__)

#define NTH_LOOP_0_0(...)     NTH_LOOP_BODY_0(__VA_ARGS__) NTH_LOOP_A_0
#define NTH_LOOP_0_0_C(...) , NTH_LOOP_BODY_0(__VA_ARGS__) NTH_LOOP_A_0
#define NTH_LOOP_A_0(...)   , NTH_LOOP_BODY_0(__VA_ARGS__) NTH_LOOP_B_0
#define NTH_LOOP_B_0(...)   , NTH_LOOP_BODY_0(__VA_ARGS__) NTH_LOOP_A_0
#define NTH_LOOP_0_0_END
#define NTH_LOOP_0_0_END_C
#define NTH_LOOP_0_0_C_END
#define NTH_LOOP_0_0_C_END_C ,
#define NTH_LOOP_A_0_END
#define NTH_LOOP_A_0_END_C ,
#define NTH_LOOP_B_0_END
#define NTH_LOOP_B_0_END_C ,
#define NTH_LOOP_BODY_0(arg0_, ...) DEPAREN(arg0_)

#define NTH_LOOP_0_1(...)     NTH_LOOP_BODY_1(__VA_ARGS__) NTH_LOOP_A_1
#define NTH_LOOP_0_1_C(...) , NTH_LOOP_BODY_1(__VA_ARGS__) NTH_LOOP_A_1
#define NTH_LOOP_A_1(...)   , NTH_LOOP_BODY_1(__VA_ARGS__) NTH_LOOP_B_1
#define NTH_LOOP_B_1(...)   , NTH_LOOP_BODY_1(__VA_ARGS__) NTH_LOOP_A_1
#define NTH_LOOP_0_1_END
#define NTH_LOOP_0_1_END_C
#define NTH_LOOP_0_1_C_END
#define NTH_LOOP_0_1_C_END_C ,
#define NTH_LOOP_A_1_END
#define NTH_LOOP_A_1_END_C ,
#define NTH_LOOP_B_1_END
#define NTH_LOOP_B_1_END_C ,
#define NTH_LOOP_BODY_1(arg0_, arg1_, ...) DEPAREN(arg1_)

#define NTH_LOOP_0_2(...)     NTH_LOOP_BODY_2(__VA_ARGS__) NTH_LOOP_A_2
#define NTH_LOOP_0_2_C(...) , NTH_LOOP_BODY_2(__VA_ARGS__) NTH_LOOP_A_2
#define NTH_LOOP_A_2(...)   , NTH_LOOP_BODY_2(__VA_ARGS__) NTH_LOOP_B_2
#define NTH_LOOP_B_2(...)   , NTH_LOOP_BODY_2(__VA_ARGS__) NTH_LOOP_A_2
#define NTH_LOOP_0_2_END
#define NTH_LOOP_0_2_END_C
#define NTH_LOOP_0_2_C_END
#define NTH_LOOP_0_2_C_END_C ,
#define NTH_LOOP_A_2_END
#define NTH_LOOP_A_2_END_C ,
#define NTH_LOOP_B_2_END
#define NTH_LOOP_B_2_END_C ,
#define NTH_LOOP_BODY_2(arg0_, arg1_, arg2_, ...) DEPAREN(arg2_)

#define NTH_LOOP_0_3(...)     NTH_LOOP_BODY_3(__VA_ARGS__) NTH_LOOP_A_3
#define NTH_LOOP_0_3_C(...) , NTH_LOOP_BODY_3(__VA_ARGS__) NTH_LOOP_A_3
#define NTH_LOOP_A_3(...)   , NTH_LOOP_BODY_3(__VA_ARGS__) NTH_LOOP_B_3
#define NTH_LOOP_B_3(...)   , NTH_LOOP_BODY_3(__VA_ARGS__) NTH_LOOP_A_3
#define NTH_LOOP_0_3_END
#define NTH_LOOP_0_3_END_C
#define NTH_LOOP_0_3_C_END
#define NTH_LOOP_0_3_C_END_C ,
#define NTH_LOOP_A_3_END
#define NTH_LOOP_A_3_END_C ,
#define NTH_LOOP_B_3_END
#define NTH_LOOP_B_3_END_C ,
#define NTH_LOOP_BODY_3(arg0_, arg1_, arg2_, arg3_, ...) DEPAREN(arg3_)

#define DECLARE_METHOD(ret_, func_, ...) \
    template <typename ... ARGS>\
    inline ret_ func_(ARGS&&... args) {\
        return execute_later(this, &func_, std::forward<ARGS>(args)...);\
    }\
    ret_ func_##_WRAPPER(PARAMS_LOOP(__VA_ARGS__))

#define DEFINE_METHOD(ret_, func_, ...) \
    ret_ func_##_WRAPPER(PARAMS_LOOP(__VA_ARGS__))

您不需要 class 的确切名称,任何解析为 class 类型的内容都将用于指向成员语法的指针。由于您在非静态成员函数中,只需将宏扩展为

template <typename ... ARGS>
int methodName(ARGS&&... args) {
    return execute_later(this, &std::remove_cv_t<std::remove_reference_t<decltype(*this)>>::methodName_WRAPPER, std::forward<ARGS>(args)...);
}

有点啰嗦,不过看在宏的后面,不会太突出。

就将所有 _WRAPPER 方法设为私有而言,这有点棘手。您当然可以充分利用 Java 并生成

public:  int foo(...) { ... }
private: int foo_WRAPPER(...) { ... }

但它会影响宏之后的任何声明,无论您如何排列其中的两个声明。这不太理想。

另一种解决方案是使用密码习惯用法。以某种方式将标记类型添加到 class 中。 CRTP是一种方式:

template<class D>
class MethodDeclarer {
    struct tag_t{ explicit tag_t() = default; };
    friend D;
};

然后结合宏使用

class Test : MethodDeclarer<Test> {
};

它给了我们什么? Test 独有的私有类型,只有它可以访问。然后您可以将宏扩展为

template <typename ... ARGS>
int methodName(ARGS&&... args) {
    return execute_later(this, &std::remove_cv_t<std::remove_reference_t<decltype(*this)>>::methodName_WRAPPER, tag_t{}, std::forward<ARGS>(args)...);
}
int methodName_WRAPPER(tag_t, int a, AMoveOnlyType b);

当然,_WRAPPER 是 public,但它需要 tag_t 才能调用,而且只有 Test 才能创建这些。这种技术实质上将访问说明符转换为要传递的值。