0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Posted at

参照を受け取る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++ではコンパイルエラーになってしまいます :sob:

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++と)でコンパイルすることができました :tada:

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?