c++pointersrvaluereturn-by-value

Given an API that returns const char*, how to get its return value's address?


I have access to an API that returns const char *, where it is guaranteed that the underlying string is valid all through the program, like below:

extern const char *getStr(); // an API that returns const char*
int main() {
    const char **p_tmp = &( getStr() ); // HOW??
}

I would like to get the address of the underlying const char * and use it throughout the program, however, as the returned value is of type const char *, instead of const char *&, getting the address of returned value gets error: lvalue required as unary '&' operand.

Now, as I only have the access to the API returning const char *, that is, I can neither change the function to return const char *& nor const char ** instead. Is there any way I can still get the address of the underlying const char *?


Conveniently asking, I did a workaround below that "persisted" the return value and use the pointers/addresses to the "persisted" const char *, is it a good practice?

static std::vector<const char*> tmp;
static int indexInTmp = 0;
extern const char* getStr();
int main() {
    tmp.emplace_back( getStr() );
    const char **p_tmp = &tmp[indexInTmp++];
    // ...other things
}

Solution

  • You are thinking too much like "C". Yes you have a "C" api, but your program will be "C++" and that means use as little raw owning pointers as possible. Also in C++ use std::string for text.

    Note : if the "C" api would return a char* (not const) you might actually have to call free on that pointer (look at the "C" API doc). By making C++ functions like this you can ensure your C++ will only solve that memory issue once.

    So you should do this:

    #include <string>
    #include <print>
    
    #include <stdio.h>
    
    const char* get_string(bool condition) // your api
    {
        if (condition)
        {
            return "Hello World!";
        }
        else
        {
            return nullptr;
        }
    }
    
    void set_string(const char* str) // your api
    {
        if (str != nullptr)
        {
            printf("C set_string: %s\n", str);
        }
    }
    
    // Wrap you "C" calls into a C++ namespace
    namespace your_api
    {
    std::string get_string(bool condition)
    {
        const char* str = ::get_string(condition);
       
        // create a std::string by copy since
        // the raw pointer from the "C" API is not guaranteed to be valid after the call
        // cannot construct a std::string from nullptr directly so be safe
        return std::string{str == nullptr ? std::string{} : std::string{str}};
    }
    
    void set_string(std::string_view sv)
    {
        // Convert std::string_view to const char* and call the C API
        ::set_string(sv.data());
    }
    
    } // namespace your_api
    
    int main()
    {
        std::string ok_str = your_api::get_string(true);
        std::string null_str = your_api::get_string(false);
    
        std::print("ok_str: {}\n", ok_str);
        std::print("null_str: {}\n", null_str);
    
        std::string cpp_string{"Hello from C++!"};
        your_api::set_string(cpp_string);
    }