c++compiler-errorsnamespacesctypesextern

How to use the same function name in two different header files with both "extern C" for Python ctypes and namespaces in C++?


Problem

I want to define two functions with the same name in different header files, such that:

A) Each function can be called by a C++ program (using namespaces to avoid conflicts).

B) The functions can be used with Python ctypes, which requires extern "C" to prevent name mangling.

While A is easily achievable using namespaces, B requires extern "C", which discards the C++ namespace information, resulting in a redefinition error during compilation.

Example:

one.h

namespace one{
    extern "C" void free(void *ptr);
}

two.h

namespace two{
    extern "C" void free(void *ptr);
}

This works fine in C++ code using namespaces to differentiate between one::free() and two::free(). However, when I add extern "C" to make the functions accessible from Python ctypes, I get a compiler error due to redefinition.

Workaround

I know I could use unique function names for each function, but I’m looking for a way to use the same function name for both C++ and Python ctypes.

Is there a way to have the same function name in different header files, accessible by both Python ctypes and C++ library calls, without causing this redefinition error?


Solution

  • To use lib1::func and lib2::func you must have the C++ versions mangled (no extern "C").

    You can have a mangled C++ symbol with the namespace and an extern "C" unmangled symbol with the same name, then you can make the C function call the C++ one, the compiler should inline the C++ one inside the C version. (unfortunately symbol renaming doesn't work with mangled C++ functions)

    // lib1.h
    namespace lib1
    {
        void func(void* ptr);
    }
    
    // lib1.cpp
    void lib1::func(void* ptr)
    {
        std::cout << "lib1 func called\n";
    }
    
    extern "C" void func(void* ptr);
    
    extern "C" void func(void* ptr)
    {
        lib1::func(ptr);
    }
    

    Note that the extern "C" one is in the .cpp file not the header, it is only exported from the shared object, but we don't allow anyone to use it in the C++ land.

    Repeating the same thing in lib2 then in C++ land you can use lib1::func or lib2::func without any issues, and using ctypes you can use lib1.func or lib2.func without issues. (because python loads shared objects with RTLD_LOCAL).

    The only limitation is that you cannot statically link lib1 and lib2 otherwise you will get repeated symbol error from the unmangled func (you can get around this by only defining the unmangled symbol in the shared objects), also calling ::func from C++ is undefined behavior (ODR violation) and will likely depend on where it is used.

    Note: C already has a global free so avoid using this name as it will clash with the global free, and will fail to compile.