参照を受け取るvoid関数を値を返す関数に変換する
呼び出し可能な場合だけ呼び出しを行う
これらを組み合わせてこういうことをしたかったのが当時の目標でした。
#include <type_traits>
#include <functional>
#include <memory>
// lib
template<class R, class T, class... Args>
R FirstArgReturnerMemfn(std::function<void(T&, R&, Args...)> F, T& inst, Args... args)
{
R r;
F(inst, r, args...);
return r;
}
template<typename R, typename T, typename... Args>
struct FirstArgReturner
{
template<void (T::*F)(R&, Args...)>
static R WithFunc(T& inst, Args... args)
{
R r;
(inst.*F)(r, args...);
return r;
}
template<void (T::*F)(R&, Args...)>
static R SharedWithFunc(T& inst, Args... args)
{
std::shared_ptr<R> r(new R);
(inst.*F)(*r, args...);
return r;
}
};
template <typename T>
constexpr bool false_v = false;
// should be 0 by default (set to 1 for local compile testing)
#define CHECK_MISSING_DEF 1
#if CHECK_MISSING_DEF
#define STRINGIFY(n) #n
#define TOSTRING(n) STRINGIFY(n)
#define MISSING_DEF_CHECKER else{static_assert(false_v<M>, "bad T in line " TOSTRING(__LINE__));}
#else
#define MISSING_DEF_CHECKER
#endif
// delay value_ to be instantiated by using two-phase name lookup (note: useless if not using SFINAE)
template<typename M, auto value_>
struct TwoPhaseValue
{
constexpr static decltype(value_) value = value_;
};
// prog
#include <cstdio>
struct A{
void fi(int &x, int n, int m){printf("CALL %d\n", n); x=n;}
};
template<typename M>
void impl(M m){
if constexpr(std::is_invocable<decltype(&A::fi), A&, int&, int>::value){
A a;
std::invoke(TwoPhaseValue<M, &FirstArgReturner<int, A, int>::WithFunc<&A::fi>>::value, a, 0);
}
else if constexpr(std::is_invocable<decltype(&A::fi), A&, int&, int, int>::value){
A a;
std::invoke(TwoPhaseValue<M, &FirstArgReturner<int, A, int, int>::WithFunc<&A::fi>>::value, a, 0, 0);
} MISSING_DEF_CHECKER
}
int main(){
void *p = NULL;
impl(p);
}
しかし、こちら、g++ではコンパイルできますが、clang++ではコンパイルエラーになってしまいます
prog.cc:66:38: error: non-type template parameter 'value_' with type 'auto' has incompatible initializer of type '<overloaded function type>'
std::invoke(TwoPhaseValue<M, &FirstArgReturner<int, A, int>::WithFunc<&A::fi>>::value, a, 0);
もっと早く気づいておくべきではあったのですが。
ところでconstexpr ifにはC++17が必要ですが、C++17であれば、関数ポインタをtemplate引数に取れるので、テンプレートクラス内のテンプレート関数とかの型解決を頑張る必要がなくなります…。
というわけで簡略化したものがこちらです。
#include <type_traits>
#include <functional>
#include <memory>
// lib
template<typename Ret, auto Func, typename Cls, typename... Args>
struct FirstArgReturnerV2
{
static Ret value(Cls& inst, Args... args)
{
Ret r;
std::invoke(Func, inst, r, args...);
return r;
}
static std::shared_ptr<Ret> valueShared(Cls& inst, Args... args)
{
std::shared_ptr<Ret> r(new Ret);
std::invoke(Func, inst, *r, args...);
return r;
}
};
template <typename T>
constexpr bool false_v = false;
// should be 0 by default (set to 1 for local compile testing)
#define CHECK_MISSING_DEF 1
#if CHECK_MISSING_DEF
#define STRINGIFY(n) #n
#define TOSTRING(n) STRINGIFY(n)
#define MISSING_DEF_CHECKER else{static_assert(false_v<M>, "bad T in line " TOSTRING(__LINE__));}
#else
#define MISSING_DEF_CHECKER
#endif
// delay value_ to be instantiated by using two-phase name lookup (note: useless if not using SFINAE)
template<typename M, auto value_>
struct TwoPhaseValue
{
constexpr static decltype(value_) value = value_;
};
// prog
#include <cstdio>
struct A{
void fi(int &x, int n, int m){printf("CALL %d\n", n); x=n;}
};
template<typename M>
void impl(M m){
if constexpr(std::is_invocable<decltype(&A::fi), A&, int&, int>::value){
A a;
std::invoke(TwoPhaseValue<M, &FirstArgReturnerV2<int, &A::fi, A, int>::value>::value, a, 0);
}
else if constexpr(std::is_invocable<decltype(&A::fi), A&, int&, int, int>::value){
A a;
std::invoke(TwoPhaseValue<M, &FirstArgReturnerV2<int, &A::fi, A, int, int>::value>::value, a, 0, 0);
} MISSING_DEF_CHECKER
}
int main(){
void *p = NULL;
impl(p);
}
無事にclang++(とg++と)でコンパイルすることができました