locked
Getting the address of a reference. RRS feed

  • Question

  • Since a reference is a special kind of objects which holds the address of another object and often is not ever allocated, we cannot get the address of a reference using "&" operator.

     

    I think there is a situation when we really need the address of the location which is used as a reference. For example, let us try to implement an old-fashioned function having a variable number of arguments, like this:

    void MyPrintf(const std::string & format, ...)

    The implementation of such function will use the well-known va_start macro:

    void MyPrintf(const std::string & format, ...)

    {

        va_list argptr;

        va_start(argptr, format); // incorrect initialization of argptr

        . . . . .

    }

    Since va_start executes internally "&format", it does not store to argptr the expected address of stack’s locations. Therefore it seems we cannot further navigate through the rest of passed arguments using subsequent va_arg macro. So it looks impossible to implement this function.

     

    I suppose the problem can be solved using an intermediate helper class (changing the signature of the function but not the calling form), or maybe by some assembler insertions.

     

    Hopping this is the right forum, I dare ask: How to navigate through the arguments of a function having variable number of arguments, if the last fixed argument is a reference? Is it possible to solve this problem without changing the signature of the function?

    Thank you.

    Wednesday, November 8, 2006 9:18 AM

Answers

  • As you probably know already references act indentical with the referenced variable so whatever you will do to the reference you'll actually do the referenced variable. You would need some special operator that does not exist in the language. Note that using va_arg macro will also need the size of the argument but using sizeof on a string reference will actually result in the sizeof of the string object itself and not in the size of the reference.

    In short this is not possibile unless you start doing some inline assembly wizardry but I'd say that this won't really be a solution but a hack. You have to assume that the compiler actually pass something like looks like a pointer for a reference.

     

    Wednesday, November 8, 2006 10:32 AM
  •  Viorel. wrote:

    I suppose the problem can be solved [...], or maybe by some assembler insertions.

    Not without being aware of the calling convention. Your above example can be solved like:

    void MyPrintf(const std::string& format, ...)
    {
      va_list argptr = (va_list)_AddressOfReturnAddress() + sizeof(DWORD_PTR) * 2;

    If there are more parameters, or the function resides within a class, the approach would have to be adjusted.

     

    Wednesday, November 8, 2006 12:59 PM
  • Mike, Einar,

     

    Thank you for valuable answers.

     

    Fortunately the described situation does not appear frequently.

     

    Though, rather than insist on original signature and use certain distinctive approach, I would prefer to revise the definition of the function in order to get rid of the reference:

    void MyPrintf( HelperClass format, ...)

    were HelperClass is an intermediate class which simply keeps a reference to passed argument:

    struct HelperClass

    {

        HelperClass(const std::string & f)

        : mF(f)

        {

        }

     

        const std::string & mF;
    };

    Now the va_start macro, applied to this structure, will give a correct address of the stack frame.

     

    The new shape of the function does not affect the simplicity of calling expressions.

     

    Probably any additional actions introduced by the helper class will be removed by the optimizer.

     

    Wednesday, November 8, 2006 4:32 PM
  • All replies

    • Hi,
      the va functions are not designed to work with c++ STL strings, you should use:
      va_start(argptr, format.c_str());

      Wednesday, November 8, 2006 10:14 AM
    • As you probably know already references act indentical with the referenced variable so whatever you will do to the reference you'll actually do the referenced variable. You would need some special operator that does not exist in the language. Note that using va_arg macro will also need the size of the argument but using sizeof on a string reference will actually result in the sizeof of the string object itself and not in the size of the reference.

      In short this is not possibile unless you start doing some inline assembly wizardry but I'd say that this won't really be a solution but a hack. You have to assume that the compiler actually pass something like looks like a pointer for a reference.

       

      Wednesday, November 8, 2006 10:32 AM
    • n0n4m3, this cannot work because va_start needs the address of argument in the call stack and c_str() returns some address in dynamically allocated memory.

       

      Wednesday, November 8, 2006 10:37 AM
    • You're right Mike.
      Viorel, sorry for misleading you.
      Wednesday, November 8, 2006 11:00 AM
    •  Viorel. wrote:

      I suppose the problem can be solved [...], or maybe by some assembler insertions.

      Not without being aware of the calling convention. Your above example can be solved like:

      void MyPrintf(const std::string& format, ...)
      {
        va_list argptr = (va_list)_AddressOfReturnAddress() + sizeof(DWORD_PTR) * 2;

      If there are more parameters, or the function resides within a class, the approach would have to be adjusted.

       

      Wednesday, November 8, 2006 12:59 PM
  • Mike, Einar,

     

    Thank you for valuable answers.

     

    Fortunately the described situation does not appear frequently.

     

    Though, rather than insist on original signature and use certain distinctive approach, I would prefer to revise the definition of the function in order to get rid of the reference:

    void MyPrintf( HelperClass format, ...)

    were HelperClass is an intermediate class which simply keeps a reference to passed argument:

    struct HelperClass

    {

        HelperClass(const std::string & f)

        : mF(f)

        {

        }

     

        const std::string & mF;
    };

    Now the va_start macro, applied to this structure, will give a correct address of the stack frame.

     

    The new shape of the function does not affect the simplicity of calling expressions.

     

    Probably any additional actions introduced by the helper class will be removed by the optimizer.

     

    Wednesday, November 8, 2006 4:32 PM