function signature and reference parameter

Discussion in 'C++' started by Ralf Goertz, Dec 2, 2010.

  1. Ralf Goertz

    Ralf Goertz Guest

    Hi,

    I am writing a library which will be used via dlopen and friends. I
    noticed that I can use

    extern "C" { //to avoid name mangling
    void foo(std::string &s) {
    std::cout<<s<<std::endl;
    s="bar";
    }
    }

    in the library and

    typedef void (*foo_t)(std::string);
    foo_t foo = dlysm(handle,"foo");

    in the calling program. Of course if I do so, calling foo(s) in the
    calling program doesn't change s. But I had expected a segfault because
    the signatures aren't the same. I assume this works because with my
    compiler (g++) foo(std::string) and foo(std::string&) are essentially
    the same but if the nonref'd form is used the parameter will be copied
    and the function will be called on with the address of the copy instead
    of the original.

    My questions are: Can I rely on this? I might want foo to sometimes
    change the parameter and sometimes not. On the other hand, dlopen and Co
    are C functions. Am I allowed to use references at all or am I just
    lucky that it works?

    Thanks,

    Ralf
    Ralf Goertz, Dec 2, 2010
    #1
    1. Advertising

  2. On 12/2/2010 7:52 AM, Ralf Goertz wrote:
    > I am writing a library which will be used via dlopen and friends. I
    > noticed that I can use
    >
    > extern "C" { //to avoid name mangling
    > void foo(std::string&s) {
    > std::cout<<s<<std::endl;
    > s="bar";
    > }
    > }
    >
    > in the library and
    >
    > typedef void (*foo_t)(std::string);


    Why can't you declare it as accepting a reference as its argument? Is
    the mismatch intentional?

    > foo_t foo = dlysm(handle,"foo");

    ^^^^^
    dlsym, I presume.

    > in the calling program. Of course if I do so, calling foo(s) in the
    > calling program doesn't change s. But I had expected a segfault because
    > the signatures aren't the same.


    Signatures do not exist at run-time.

    > I assume this works because with my
    > compiler (g++) foo(std::string) and foo(std::string&) are essentially
    > the same but if the nonref'd form is used the parameter will be copied
    > and the function will be called on with the address of the copy instead
    > of the original.


    It "works" because when you declare a function 'extern "C"', the
    argument information is removed, doesn't exist. It's up to you to
    convert the pointer you get from 'dlsym' to the *correct* one. If you
    do not, your program has undefined behavior, most likely. See the
    documentation on 'dlsym'.

    > My questions are: Can I rely on this?


    On what, exactly?

    > I might want foo to sometimes
    > change the parameter and sometimes not. On the other hand, dlopen and Co
    > are C functions. Am I allowed to use references at all or am I just
    > lucky that it works?


    'dlopen and Co' are not "C functions", they are Un!x functions (or
    Posix, I don't [care to] remember). Consider asking about them in a
    Un!x newsgroup.

    The manipulation you perform with your function pointers is akin to the
    same for object pointers which you can convert to 'void*' or to the
    integral type suitable for that purpose, and then back. For the return
    path you use 'static_cast' for the void* or 'reinterpret_cast' for the
    integral value. But that is guaranteed to work if you convert to the
    proper type as the result. The language provides no guarantees if you
    take a pointer to one type, and after the round-trip, so to speak, get
    another type. Same with functions. If your original function has a
    reference as its argument, the pointer to function needs to have that
    exact argument type, or anything can happen (see "nasal demons").

    V
    --
    I do not respond to top-posted replies, please don't ask
    Victor Bazarov, Dec 2, 2010
    #2
    1. Advertising

  3. Ralf Goertz

    Marc Guest

    Ralf Goertz wrote:

    > I am writing a library which will be used via dlopen and friends. I
    > noticed that I can use
    >
    > extern "C" { //to avoid name mangling
    > void foo(std::string &s) {
    > std::cout<<s<<std::endl;
    > s="bar";
    > }
    > }
    >
    > in the library and
    >
    > typedef void (*foo_t)(std::string);
    > foo_t foo = dlysm(handle,"foo");


    In addition to Victor's answer, don't forget to do the foo_t typedef
    inside an extern "C" block.
    Marc, Dec 2, 2010
    #3
  4. Ralf Goertz

    Ralf Goertz Guest

    Victor Bazarov wrote:

    > On 12/2/2010 7:52 AM, Ralf Goertz wrote:
    >> I am writing a library which will be used via dlopen and friends. I
    >> noticed that I can use
    >>
    >> extern "C" { //to avoid name mangling
    >> void foo(std::string&s) {
    >> std::cout<<s<<std::endl;
    >> s="bar";
    >> }
    >> }
    >>
    >> in the library and
    >>
    >> typedef void (*foo_t)(std::string);

    >
    > Why can't you declare it as accepting a reference as its argument? Is
    > the mismatch intentional?


    At first it was a mistake and then I thought I can get to different
    functions by casting the pointer to different function types. Which was
    the case and which made me wonder whether it is safe to use references
    at all in such a fashion.

    >> foo_t foo = dlysm(handle,"foo");

    > ^^^^^
    > dlsym, I presume.


    Of course.

    > It "works" because when you declare a function 'extern "C"', the
    > argument information is removed, doesn't exist. It's up to you to
    > convert the pointer you get from 'dlsym' to the *correct* one. If you
    > do not, your program has undefined behavior, most likely.


    I didn't know that, thanks.

    >> My questions are: Can I rely on this?

    >
    > On what, exactly?


    On the fact that the compiled library is the same whether or not I use
    references for the parameters. At least that's what happens on my
    system. Whether the parameter is changed depends only on how I cast the
    function pointer in the calling program. But as you pointed out that is
    probably UB:

    > > I might want foo to sometimes
    >> change the parameter and sometimes not. On the other hand, dlopen and Co
    >> are C functions. Am I allowed to use references at all or am I just
    >> lucky that it works?

    >
    > 'dlopen and Co' are not "C functions", they are Un!x functions (or
    > Posix, I don't [care to] remember). Consider asking about them in a
    > Un!x newsgroup.


    What I meant is: Can I use referenced parameters in a library using
    extern "C" function declarations? After all they might be used by C
    programs (with dlopen and Co) which know nothing about references.

    > The manipulation you perform with your function pointers is akin to the
    > same for object pointers which you can convert to 'void*' or to the
    > integral type suitable for that purpose, and then back. For the return
    > path you use 'static_cast' for the void* or 'reinterpret_cast' for the
    > integral value. But that is guaranteed to work if you convert to the
    > proper type as the result. The language provides no guarantees if you
    > take a pointer to one type, and after the round-trip, so to speak, get
    > another type. Same with functions. If your original function has a
    > reference as its argument, the pointer to function needs to have that
    > exact argument type, or anything can happen (see "nasal demons").


    Thanks for the explanation.

    Ralf
    Ralf Goertz, Dec 2, 2010
    #4
  5. On 12/2/2010 8:36 AM, Ralf Goertz wrote:
    > Victor Bazarov wrote:
    >
    >> On 12/2/2010 7:52 AM, Ralf Goertz wrote:
    >>> I am writing a library which will be used via dlopen and friends. I
    >>> noticed that I can use
    >>>
    >>> extern "C" { //to avoid name mangling
    >>> void foo(std::string&s) {
    >>> std::cout<<s<<std::endl;
    >>> s="bar";
    >>> }
    >>> }
    >>>
    >>> in the library and
    >>>
    >>> typedef void (*foo_t)(std::string);

    >>
    >> Why can't you declare it as accepting a reference as its argument? Is
    >> the mismatch intentional?

    >
    > At first it was a mistake and then I thought I can get to different
    > functions by casting the pointer to different function types. Which was
    > the case and which made me wonder whether it is safe to use references
    > at all in such a fashion.


    Since references are a C++ concept, then the only "safe" way of using
    them would be to call your function from C++ and implement it in C++.

    > [..]
    >>> My questions are: Can I rely on this?

    >>
    >> On what, exactly?

    >
    > On the fact that the compiled library is the same whether or not I use
    > references for the parameters. At least that's what happens on my
    > system. Whether the parameter is changed depends only on how I cast the
    > function pointer in the calling program. But as you pointed out that is
    > probably UB:


    Yes, it is. And by the definition of UB, even if it seems to work on
    your system today, there is no guarantee it's going to work tomorrow
    even if you don't recompile/relink your program.

    >
    >> > I might want foo to sometimes
    >>> change the parameter and sometimes not. On the other hand, dlopen and Co
    >>> are C functions. Am I allowed to use references at all or am I just
    >>> lucky that it works?

    >>
    >> 'dlopen and Co' are not "C functions", they are Un!x functions (or
    >> Posix, I don't [care to] remember). Consider asking about them in a
    >> Un!x newsgroup.

    >
    > What I meant is: Can I use referenced parameters in a library using
    > extern "C" function declarations? After all they might be used by C
    > programs (with dlopen and Co) which know nothing about references.


    You can use references as arguments in a library, but the limitation is
    that you can't use those from the language that doesn't define them. If
    those functions "might" be used by a caller from the C language, I am
    not sure how they are going to avoid UB. They can't cast the pointer
    they get from 'dlsym' to the proper type - it doesn't exist.

    Good luck!

    V
    --
    I do not respond to top-posted replies, please don't ask
    Victor Bazarov, Dec 2, 2010
    #5
  6. Ralf Goertz

    SG Guest

    On 2 Dez., 14:36, Ralf Goertz wrote:
    > Victor Bazarov wrote:

    [...]
    > >> My questions are: Can I rely on this?

    >
    > > On what, exactly?

    >
    > On the fact that the compiled library is the same whether or not I use
    > references for the parameters. At least that's what happens on my
    > system. Whether the parameter is changed depends only on how I cast the
    > function pointer in the calling program. But as you pointed out that is
    > probably UB:


    It works in your case because of the (current) G++ ABI. This is an
    implementation-specific detail. Objects of "complicated types" are
    always passed by reference to a function under the hood and the caller
    has to locally create a temporary copy if the function was declared to
    take the parameter by value. Similarly, objects of "complicated types"
    are returned from functions by in-place construction in an
    uninitialized memory area which is given by an additional (hidden)
    pointer parameter. This kind of binary interface allows many of the
    copy elisions (including return value optimization) which the C++
    standard explicitly allows.

    But again: This is all implementation-specific. According to the C++
    standard, invoking a function via a pointer where the pointer's type
    doesn't match the function's actual type is definitely undefined
    behaviour.

    Cheers!
    SG
    SG, Dec 2, 2010
    #6
  7. Ralf Goertz

    Goran Guest

    On Dec 2, 1:52 pm, Ralf Goertz <> wrote:
    > Hi,
    >
    > I am writing a library which will be used via dlopen and friends. I
    > noticed that I can use
    >
    > extern "C" { //to avoid name mangling
    >         void foo(std::string &s) {
    >                 std::cout<<s<<std::endl;
    >                 s="bar";
    >         }
    >
    > }
    >
    > in the library and
    >
    > typedef void (*foo_t)(std::string);
    > foo_t foo = dlysm(handle,"foo");
    >
    > in the calling program. Of course if I do so, calling foo(s) in the
    > calling program doesn't change s. But I had expected a segfault because
    > the signatures aren't the same. I assume this works because with my
    > compiler (g++) foo(std::string) and foo(std::string&) are essentially
    > the same but if the nonref'd form is used the parameter will be copied
    > and the function will be called on with the address of the copy instead
    > of the original.
    >
    > My questions are: Can I rely on this? I might want foo to sometimes
    > change the parameter and sometimes not. On the other hand, dlopen and Co
    > are C functions. Am I allowed to use references at all or am I just
    > lucky that it works?


    Works by luck.

    First off, given that you are using C++ interface, you should be aware
    that interfacing works with one implementation and one version of
    runtime libraries. If so, you might just as well use decorated name.

    If you don't want to do it, it's then probbably better to have one
    plain C entry point that you use with dlsym, and you use that to
    dispatch C++ functions out (e.g. using an enum). e.g.

    extern "C"
    {
    typedef enum public_functions_
    {
    f1,f2,f3
    } public_functions;
    void* get_func(public_functions);
    }

    Goran.
    Goran, Dec 2, 2010
    #7
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. JJBW
    Replies:
    1
    Views:
    10,041
    Joerg Jooss
    Apr 24, 2004
  2. Gustavo Narea
    Replies:
    14
    Views:
    847
    Gustavo Narea
    Feb 16, 2009
  3. Travis Parks

    Checking Signature of Function Parameter

    Travis Parks, Aug 28, 2011, in forum: Python
    Replies:
    10
    Views:
    343
    Ethan Furman
    Aug 30, 2011
  4. AzamSharp
    Replies:
    2
    Views:
    157
  5. daniel rich
    Replies:
    5
    Views:
    302
    daniel rich
    Nov 14, 2012
Loading...

Share This Page