参照を受け取るvoid関数を値を返す関数、を呼び出し可能な場合だけ呼び出すとclangではコンパイルエラーになっていた(修正済)

参照を受け取るvoid関数を値を返す関数に変換する

https://qiita.com/cielavenir/items/f2caf40eb49ddf20571a

呼び出し可能な場合だけ呼び出しを行う

https://qiita.com/cielavenir/items/07980ef6a105ef931df2

これらを組み合わせてこういうことをしたかったのが当時の目標でした。

```cpp
#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++ではコンパイルエラーになってしまいます :sob:

https://wandbox.org/permlink/YoqKxasopS1bGlcS

```
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引数に取れるので、テンプレートクラス内のテンプレート関数とかの型解決を頑張る必要がなくなります…。

というわけで簡略化したものがこちらです。

```cpp
#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);
}
```

https://wandbox.org/permlink/gMHU59myw29Bsk2Z

無事にclang++(とg++と)でコンパイルすることができました :tada: