C++
メタプログラミング
C+11

scoped enum用オーバーロード関数

はじめに

今回開発時にログ用のオーバーロード関数を作成していました.しかし,scopedenumで定義した型用の関数を全種分定義するわけにもいかず,どうにかならいなかと思って調べた結果の備忘録です.

サンプルコード

main.cpp
#include <stdio.h>
#include <vector>

// retrun macro
#define RET_MACRO(retData){\
    print(__func__, retData);\
    return retData;\
}
#define RET_VOICE_MACRO(){\
    print(__func__);\
    return;\
}

enum class Hoge
{
    A = 0,
    B,
    C,
};

// scoped enum
template<typename T>
void print(const char* funcName
, T num
, typename std::enable_if<std::is_enum<T>::value, std::nullptr_t>::type = nullptr)
{
    printf("%-20s : %d\n", funcName, num);
}
// double
void print(const char* funcName, double num)
{
    printf("%-20s : %f\n", funcName, num);
}
// int
void print(const char* funcName, int num)
{
    printf("%-20s : %d\n", funcName, num);
}
// vector
template<typename T>
struct is_vector : std::false_type {};
template<typename T>
struct is_vector<std::vector<T>> : std::true_type {};
template<typename T>
void print(const char* funcName
, T num
, typename std::enable_if<is_vector<T>::value, std::nullptr_t>::type = nullptr)
{
    printf("%-20s : vec\n", funcName);
}
// other
void print(const char* funcName, ...)
{
    printf("%-20s : --\n", funcName);
}


// return int
int retIntFuc()
{
    RET_MACRO(5);
}
// return double
double retDoubleFuc()
{
    RET_MACRO(0.5);
}

// return scoped enum
Hoge retScopedEnumFuc()
{
    RET_MACRO(Hoge::B);
}

// return vector
std::vector<int> retVectorFunc()
{
    std::vector<int> vec;
    RET_MACRO(vec);
}

// retrun other
struct Othr
{
    int a;
    int b;
};
Othr retOtherFunc()
{
    Othr other = {};
    RET_MACRO(other);
}

// retrun other
void retVoidFunc()
{
    RET_VOICE_MACRO();
}

int main()
{
    retIntFuc();
    retDoubleFuc();
    retScopedEnumFuc();
    retVectorFunc();
    retOtherFunc();
    retVoidFunc();

    return 0;
}
// return double
double retDoubleFuc()
{
    RET_MACRO(0.5);
}

// return scoped enum
Hoge retScopedEnumFuc()
{
    RET_MACRO(Hoge::B);
}

// return vector
std::vector<int> retVectorFunc()
{
    std::vector<int> vec;
    RET_MACRO(vec);
}

// retrun other
struct Othr
{
    int a;
    int b;
};
Othr retOtherFunc()
{
    Othr other = {};
    RET_MACRO(other);
}

// retrun other
void retVoidFunc()
{
    RET_VOICE_MACRO();
}

int main()
{
    retIntFuc();
    retDoubleFuc();
    retScopedEnumFuc();
    retVectorFunc();
    retOtherFunc();
    retVoidFunc();

    return 0;
}

実行結果

retIntFuc            : 5
retDoubleFuc         : 0.500000
retScopedEnumFuc     : 1
retVectorFunc        : vec
retOtherFunc         : --
retVoidFunc          : --

解説

23~51行目までのprint関数がログ用の関数で,今回の肝となるのが22~26行目のscopedenum用のprint関数です.
大まかな流れは以下通りです.
・渡された引数の型がenumか否かを「std::is_enum」にて判定を行います.enumであればtrueを返す.
・「std::enable_if」は「is_enum」がtrueとなればenbale_ifは「std::nullptr_t」型として実態化.
・これによりenum以外の場合SFINAEによって,オーバーロード解決の候補から外され,enumのときに実行される関数ができる.

私自身少しふわっとした理解なので,間違い等あれば指摘していただけるとありがたいです.
また,vectorは後で必要になったので,参考文献4を参考にメタ関数等作成しました.

参考文献

1.enable_if - cpprefjp - C++日本語リファレンス
2.enable_if クラス
3.【C++ Advent Calendar 2016 22日目】C++ で enable_if を使うコードのベストプラクティス
4.C++メタ関数のまとめ