Pavel P
Pavel P

Reputation: 16843

GCC: Customizing printf for string output

GCC allows customization of printf specifiers. However, I don't see how I can "teach" it to accept my string class for %s specifier. My string class is a simple wrapper over char pointer and has exactly one member variable (char * data) and no virtual functions. So, it's kind of ok to pass it as-is to printf-like functions in place of regular char *. The problem is that on gcc static analyzer prevents me from doing so and I have to explicitly cast it to const char * to avoid warnings or errors.

My cstring looks something like this:

class cstring
{
   cstring() : data(NULL){}
   cstring(const char * str) : data(strdup(str)){}
   cstring(const cstring & str) : data(strdup(str.data)){}
   ~cstring()
   {
        free(data);
   }
   ...
   const char * c_str() const
   {
        return data;
   }

private:
   char * data;
};

Example code that uses cstring:

cstring str("my string");
printf("str: '%s'", str);

On GCC I get this error:
error: cannot pass objects of non-trivially-copyable type 'class cstring' through '...'
error: format '%s' expects argument of type 'char*', but argument 1 has type 'cstring' [-Werror=format]
cc1plus.exe: all warnings being treated as errors

Upvotes: 3

Views: 749

Answers (1)

Tom Seddon
Tom Seddon

Reputation: 2748

The C++ standard doesn't require compilers to support this sort of code, and not all versions of gcc support it. (https://gcc.gnu.org/onlinedocs/gcc/Conditionally-supported-behavior.html suggests that gcc-6.0 does, at least - an open question whether it will work with classes such as the one here.)

The relevant section in the C++11 standard is 5.2.2 section 7:

When there is no parameter for a given argument, the argument is passed in such a way that the receiving function can obtain the value of the argument by invoking va_arg ... Passing a potentially-evaluated argument of class type (Clause 9) having a non-trivial copy constructor, a non-trivial move constructor, or a non-trivial destructor, with no corresponding parameter, is conditionally-supported with implementation-defined semantics.

(But look on the bright side: if you get into the habit of using c_str, then at least you won't get tripped up when/if you use std::string.)

Upvotes: 2

Related Questions