skip to Main Content

For scientific purposes I’m writing some number crunching routines in C++ and benchmarking various ways of running computations for ultra-high-precision floating-point numbers. And when I decide on the best routine for a specific purpose, I’d like to specify a generalized alias to that function.

For example, to compute the natural logarithm of a number, I’ve coded three functions named LnXMacL, LnXHalley, and, LnXagm. They implement the natural logarithm via three different methods: a MacLaurin series, Halley’s method, and arithmetic-geometric mean. They all exhibit the same accuracy of several hundred decimal places, but the MacLaurin series is much faster than the other two. So rather than rename it something like LogX, for historical reference I’d rather keep the original name intact and create an alias to LnXMacL. I’d also like that alias to appear as a visible entry point in the DLL because I frequently use C# to prototype the heavy math functions, then port them to C++. The header for the three functions is as follows:

extern "C" __declspec(dllexport) void __stdcall LnXMacL(UINT64* LnX, UINT64* xM);
extern "C" __declspec(dllexport) void __stdcall LnXHalley(UINT64* LnX, UINT64* xM);
extern "C" __declspec(dllexport) void __stdcall LnXagm(UINT64* LnX, UINT64* xM);

Ideally, the alias would be the equivalent of something like:

extern "C" __declspec(dllexport) void __stdcall LogX(UINT64* LnX, UINT64* xM);

but instead of compiling as a separate function, its entry point would go directly to LnXMacL.

I’m using Visual Studio 2022 on a Windows 11 machine. I have decades of experience writing C code, but only a few years at C++. Any help will be greatly appreciated. I searched this forum extensively and found a lot on aliasing, but haven’t found anything on creating an alias to the same entry point in a DLL.

2

Answers


  1. I am assuming that you want a shared symbol that also points to the function so it can be called from C#, not a function pointer, the most portable way is to have the alias function call the original function.

    extern "C" void __stdcall LogX(UINT64* LnX, UINT64* xM);
    {
        LnXagm(LnX, xM);
    }
    

    Most compilers are going to inline this call with optimizations enabled, but you need to test that inlining happened if this code timing is critical.


    if you are only calling this function from C++ then you can create an alias in the header

    extern "C" constexpr inline auto LogX = LnXagm;
    

    this is a function pointer, so calling it from a different language is not possible, but compilers are able to inline it because it is constexpr


    There are however compiler/OS dependent tricks to alias library functions, on MSVC you can use .DEF File.

    EXPORTS
     LogX=LnXagm
    

    or there is a compiler pragma /EXPORT that you can use if you don’t want to use a .DEF file, also see How to specify a "clean" name of DLL exports?

    #pragma comment(linker, "/export:LogX=LnXagm")
    

    IMPORTANT NOTE: on x86 __stdcall adds extra mangling, which is not done in x86_64, the above pragma will not work as-is on x86, you need to find the correct mangling for LnXagm (_LnXagm@8 in this case), but if you are only targeting x86_64 the above pragma will work out of the box.

    As for gcc (mingw) there is symbol renaming using alias attribute, also see Renaming symbols at compile time without changing the code in a cross platform way

    Login or Signup to reply.
  2. As a supplementation to another answer, you can use compiler-specific attributes to force the inlining. Using C++11 attribute syntax, this can be achieved by:

    [[msvc::forceinline_calls]] on MSVC:

    extern "C" void __stdcall LogX(UINT64* LnX, UINT64* xM);
    {
        [[msvc::forceinline_calls]]
        LnXagm(LnX, xM);
    }
    

    [[clang::always_inline]] on Clang:

    extern "C" void __stdcall LogX(UINT64* LnX, UINT64* xM);
    {
        [[clang::always_inline]]
        LnXagm(LnX, xM);
    }
    

    Note that as the C++ standard requires different functions to have different addresses, this might cause size bloat in the binary. The bloating can be solved by employing -ffunction-sections + -Wl,--icf=safe (possibly also -fuse-ld=lld) to utilize the lld linker ability on Clang. Manipulating exported symbols using MSVC .DEF or GNU alias attribute doesn’t have such concern.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search