1

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?

8
  • 2
    How do you plan to distinguish these functions on the ctypes end? Commented Oct 9, 2024 at 12:50
  • The two libraries are loaded separately in python Commented Oct 9, 2024 at 12:53
  • 2
    How does it work on python side? You have some import lib1 and import lib2, and then lib1.free lib2.free? Then there shouldn't be the problem with redefinitions because you don't need to link the 2 libs together? Commented Oct 9, 2024 at 12:55
  • The problem is not on the python side. It’s on the c++ side. Commented Oct 9, 2024 at 13:16
  • To use them in c++ they need to be mangled, and you cannot even rename mangled symbols, so you need to use a different function for the unmangled one (used by ctypes) and wrap the mangled function inside it ... it is more code but you need to do it to satisfy your requirements. Commented Oct 9, 2024 at 13:25

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

Comments

0

What you can try to create a wrapper function that takes a function pointer as an argument to call either one::free() or two::free().

extern "C" {
    typedef void (*free_func)(void*);

    void call_free(free_func func, void* ptr) {
        func(ptr);
    }
}

namespace one{
    void free(void *ptr);
}

namespace two{
    void free(void *ptr);
}

Example of usage for C++:

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

    void call_two_free(void* ptr) {
        two::free(ptr);
    }
}

int main() {
    void* ptr = new int;

    call_free(&call_one_free, ptr);
    call_free(&call_two_free, ptr);

    delete static_cast<int*>(ptr);

    return 0;
}

Example of usage in Python with ctypes:

import ctypes

lib = ctypes.CDLL('your_library.so')

# Define the function prototype for call_free
lib.call_free.argtypes = [ctypes.CFUNCTYPE(None, ctypes.c_void_p), ctypes.c_void_p]


def free_one(ptr):
    lib.call_one_free(ptr)

def free_two(ptr):
    lib.call_two_free(ptr)

ptr = lib.malloc(ctypes.sizeof(ctypes.c_int))  # Allocate memory in C
lib.call_free(free_one, ptr)
lib.call_free(free_two, ptr)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.