简体   繁体   中英

C++: Is it safe to use a va_list type as a class member?

Is it safe to use a va_list type as a class member?

The below example works in Solaris. It doesn't need to be portable across different operating systems. But is it portable to future Solaris versions? Different HW? Different compilers? Or compiler options?

class MyFormatString
{
public:
     MyFormatString(const char* formatString, va_list varg);
     ~MyFormatString() { va_end(mVarg); }
     // ... 
     // provide some util functions to operate on the format string
     // ...
private:
     string mFormatString;
     va_list mVarg;
};

MyFormatString::MyFormatString(const char* fmt, va_list varg)
{
    if (fmt)
        mFormatString=fmt;

    va_copy(mVarg, varg);
}

No, you can only use the va_list while the objects it refers to are in scope (in the function itself, or passed as an argument to other functions), up to the point at which you call va_end . You must call va_end before returning from the function and, after that point, the va_list is no longer usable.

From C99, 7.15.1.3: "if the va_end macro is not invoked before the return, the behavior is undefined."

In C++11, consider variadic templates or std::tuple as type-safe alternatives to old-school variadic functions.

This is not safe on any platform; va_list points to the stack arguments which will be obsolete when the constructor returns. Consider using a more C++-ish type-safe method to store the arguments; probably storing in a std::vector<std::string> .

This will probably work as expected as long as all MyFormatString instances are destroyed before the corresponding variadic function returns.

However, it's still undefined behaviour according to the C standard as va_end() needs to be called before the function which invoked va_copy() returns, though I don't know of any implementations where it doesn't suffice to call va_end() before the va_list goes out of scope or the corresponding variadic function returns.

If you're looking for a standards-compliant solution, use a va_list* instead of a copy of the list. This will share state between MyFormatString instances created from the same va_list , though, so it is not really equivalent.

If you do not want to share state, you'll need to manually create a copy of the va_list and pass a pointer to that to your constructor.

Using pointers has issues on platforms where va_list is an array type, which can be avoided by using C++ references instead (which obviously aren't accounted for in the C standard, but should behave according to pointer semantics).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM