C++ 自动计算函数签名

August 11, 2021

最近研究 emscripten 的 binding 实现,发现个自动计算函数签名的实现,挺有意思,直接看代码:

https://github.com/emscripten-core/emscripten/blob/f099e309d6ab1b2ec6e1967a72d5e4e90ea19abf/system/include/emscripten/bind.h#L397

////////////////////////////////////////////////////////////////////////////////
// SignatureCode, SignatureString
////////////////////////////////////////////////////////////////////////////////

namespace internal {

template<typename T>
struct SignatureCode {};

template<>
struct SignatureCode<int> {
    static constexpr char get() {
        return 'i';
    }
};

template<>
struct SignatureCode<void> {
    static constexpr char get() {
        return 'v';
    }
};

template<>
struct SignatureCode<float> {
    static constexpr char get() {
        return 'f';
    }
};

template<>
struct SignatureCode<double> {
    static constexpr char get() {
        return 'd';
    }
};

template<typename... Args>
const char* getGenericSignature() {
    static constexpr char signature[] = { SignatureCode<Args>::get()..., 0 };
    return signature;
}

template<typename T> struct SignatureTranslator { using type = int; };
template<> struct SignatureTranslator<void> { using type = void; };
template<> struct SignatureTranslator<float> { using type = float; };
template<> struct SignatureTranslator<double> { using type = double; };

template<typename... Args>
EMSCRIPTEN_ALWAYS_INLINE const char* getSpecificSignature() {
    return getGenericSignature<typename SignatureTranslator<Args>::type...>();
}

template<typename Return, typename... Args>
EMSCRIPTEN_ALWAYS_INLINE const char* getSignature(Return (*)(Args...)) {
    return getSpecificSignature<Return, Args...>();
}

} // end namespace internal

其中主要用到了 C++ 的模板特化和可变参数模板两个特性,模板特化产生类型对应的符号(如 int 对应 ‘i’),可变参数模板用于提取任意函数的参数列表:getSignature 的入参是一个函数指针,结合模板,巧妙地获得了各参数的类型以及返回值类型

简单测试一下:

double testFunc(int a, float m) {
    return (double)a + m;
}

int main() {
    const char *sig = internal::getSignature(testFunc);
    std::cout << sig << std::endl;
    return 0;
}

输出:

/Users/hoganliu/CLionProjects/test/cmake-build-debug/test
Fif

Process finished with exit code 0