This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Compiler: Extern "C" with function having function pointer as a parameter

Other Parts Discussed in Thread: TEST2

Tool/software: TI C/C++ Compiler

Hello!

Noticed the following on both 16.9.6.TLS and 18.1.1.LTS, when compiling

extern "C" int test(int (*)(int));

int test(int (*f)(int)) { return 0; }

The result will be the decorated function "_Z4testPFiiE", not "test", as expected, and result later in a link error.  Working around, passing the function in a structure will work, but makes the call syntax awkward from C:

struct test_arg { int (*f)(int); }

extern "C" int test2(struct test_arg f);

int test2(struct test_arg f) { return 0; }

This will correctly link correctly (the name is "test2")

  • The function pointer on the first line of the example has C linkage.  The function pointer on the last line of the example has C++ linkage.  Therefore, due to overloading, those are considered two different functions named test.

    The best fix is to use a typedef for the function pointer, so it always has C linkage ...

    extern "C" { typedef int (*pfc)(int); }
    extern "C" int test(pfc);
    int test(pfc f) { return 0; }

    While not strictly necessary, it also considered good practice to explicitly use C linkage on the function implementation ...

    extern "C" int test(pfc f) { return 0; }

    Thanks and regards,

    -George

  • Thank you -- hadn't really thought about typedefing it (which is more than ok as they actual signature is more complex!)

    However, I would point out that I think the TI compiler is possibly unique in the handling of the function (pointer) argument.  Every other compiler I tested will actually cause a multiple definition error if trying to define a function with both the typedef and the "raw" (C++) function pointer parameter (in the same translation unit), and will not decorate/mangle the function name either with the typedef or without it, so the behaviour differs from at least industry standard practice (gcc, clang, msvc, icc at least.)

    Also, calling will work with both functions as arguments without any warning, and they can be placed into variables of either type totally transparently (as expected, really.) Is the function type considered to be promoted/converted automatically in this case (or when are they considered different except when choosing the extern "C" overload ?)  The following compile without any diagnostic:

    (Havent' tried as a template parameter, but cannot really see a reason to partially specialize based on function signature.)

    extern "C" {
    
    	typedef int(*fpfunc)(int);
    
    	int testf(fpfunc, int);
    
    };
    
    
    fpfunc f_c;
    int(*f_cpp)(int);
    
    
    int testf(fpfunc f, int x)
    {
    	f_c = f;
    	f_cpp = f;
    	return f(x);
    }
    
    
    int cppfunc(int arg)
    {
    	return arg;
    }
    
    
    int passcppfunc(int x)
    {
    	return testf(cppfunc, x);
    }
    
    
    int cppfuncf(int(*fp)(int), int x)
    {
    	f_c = fp;
    	f_cpp = fp;
    	return fp(x);
    }
    
    
    int passcppfuncf(int x)
    {
    	return cppfuncf(cppfunc, x) + cppfuncf(f_c, x) + cppfuncf(f_cpp, x);
    }

    The following will only pass the TI compiler, causing a multiple definition on other compilers tested:

    #if OVERLOAD > 0
    /* WIll pass on TI, multiple definition on gcc, clang, ... others
    */
    int testf(int(*f)(int), int x)
    {
    	return f(x) + f(x);
    }
    #endif

    Oh well, thanks again for the help, fixed the issue perfectly!

  • Jaakko Eskelinen said:
    However, I would point out that I think the TI compiler is possibly unique in the handling of the function (pointer) argument.

    I think the reason is the same as the one explained in this post.  But I will check on that.

    Thanks and regards,

    -George

  • George Mock said:
    I think the reason is the same as the one explained in this post.  But I will check on that.

    Confirmed.  The reason is the same.

    Thanks and regards,

    -George