0

I have a C++ object (boost::format) that has a str() function which returns an std::string.

So when I need a formatted C string, I need to write something like:

(boost::format("%1% %2%") % "1" % "2").str().c_str()

I find that rather verbose, and I need it a lot. I thought of creating a derived class which has an operator char* and would work like this (Ch = char or wchar_t):

operator Ch const* () const
{
    return str().c_str();
}

But of course, the string returned by str() is deallocated when the function returns, and no valid C string is returned.

Is there any kind of workaround?

The workaround needs to create a string that exists as long as the surrounding function call:

lib_function((boost::format("%1% %2%") % "1" % "2").str().c_str()); 
// can be deallocated here
Felix Dombek
  • 13,664
  • 17
  • 79
  • 131

3 Answers3

4

The most obvious solution is to define a type which contains the std::string, and converts implicitly to a char const*. Something like:

class ToPlainC
{
    std::string myValue
public:
    ToPlainC( boost::format const& fmt )
        : myValue( fmt.str() )
    {
    }
    operator char const*() const
    {
        return myValue.c_str();
    }
};

which could be used:

lib_function( ToPlainC( boost::format( "%1% %2%" ) % "1" % "2" ) );

Such implicit conversions are not usually a good idea, but if you document the class well, that it should only be used for this particular scenario, I think it would be acceptable.

EDIT:

It occurs to me that to encourage only using this class as a temporary, in this particular scenario, you could name it using the naming conventions you normally use for functions, and not those you use for classes; the user would then have the impression that he was using a function, and it would stick out like a sore thumb if he used it otherwise.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • Depending on the compiler and the compiler settings, [RVO](http://en.wikipedia.org/wiki/Return_value_optimization) may or may not be exploited in initializing `ToPlainC::myValue`. What if we make myValue a const reference to string. I believe the behavior is still defined. @James Can you please share your views? – Mohit Jain Apr 29 '14 at 14:29
  • +1 This gave me the idea how to do it without any additional writing. My "cformat" class inherits from boost::function, defines `operator %` and `operator Ch const*` so now I can write `lib_function( cformat( "%1% %2%" ) % "1" % "2" );` – Felix Dombek Apr 29 '14 at 14:31
  • 1
    @MohitJain Making `myValue` a reference results in undefined behavior: initializing a const reference in an initializer list is a special case, which only extends the lifetime of the temporary for the lifetime of the constructor. (If you think about where the temporary is located in a typical implementation, and imagine that none of the functions are inlined, it should be obvious why.) And I'm not sure where RVO changes anything here. (Move semantics, on the other hand, do.) – James Kanze Apr 29 '14 at 15:02
  • @FelixDombek If you define `operator%`, then you probably don't need inheritance. Your `cformat` contains a `boost::format`, and its (template) `operator%` just forwards to the `boost::format::operator%`. (You could even take advantage of this to use something other than `%`, which is a particularly poor choice.) – James Kanze Apr 29 '14 at 15:08
  • If you intend to use `ToPlainC` as a temporary, shouldn't the conversion be ref-qualified? `operator const char*() &&` ? – MSalters Apr 29 '14 at 15:10
  • @MSalters: No, because a temporary can be bound to a const lvalue reference. – Siyuan Ren Apr 29 '14 at 15:22
  • @C.R. While that's technically true, that would not be the intended use case of `ToPlainC::operator const char*()`. – MSalters Apr 29 '14 at 15:38
  • @MSalters That might be an idea. The compiler I use doesn't support them, so I can't experiment to find out. (And of course, when I encountered a similar problem, and figured out this solution, there was no C++11.) – James Kanze Apr 29 '14 at 15:53
  • @MSalters: I see your point. Ref qualifying the conversion operator with `&&` is a strong indication and compile-time check against use outside its intentions. However, not many compilers support and correctly support ref qualifiers. Especially for those stuck with the dreaded MSVC, `const` qualifier is the only choice. – Siyuan Ren Apr 30 '14 at 01:37
1

Return an std::string on the stack. It's an extra copy, so it's a bad idea if performance is important, but it would work to eliminate most of the redundant typing.

Michael Kohne
  • 11,888
  • 3
  • 47
  • 79
1

You can define a structure like one shown below:

struct my_str
{
  const std::string &tmp;
  my_str(const boost::format &tmp) : tmp( tmp.str() ) {}
  operator const char *() const { return tmp.c_str(); }
};

And you can call it like

lib_function ( my_str(boost::format("%1% %2%") % "1" % "2") );

If you are worrying the reference would be dangling, read this article.

Mohit Jain
  • 30,259
  • 8
  • 73
  • 100
  • This won't work, for a similar reason my answer didn't - temporaries bound to constructor parameters end their lifetime when the constructor ends. It would work if you stored `tmp` by value instead of by reference, though. And RVO would most likely avoid any copying. – Angew is no longer proud of SO Apr 29 '14 at 13:42
  • I checked once [here](http://ideone.com/q2U89K) the order of construction and destruction. Do you mean to say the order behavior is undefined or unspecified? – Mohit Jain Apr 29 '14 at 13:52
  • Adding from specs of it helps understand better. **12.2 Temporary objects, clause 3**: "Temporary objects are destroyed as the last step in evaluating the full-expression (1.9) that (lexically) contains the point where they were created." **1.9 Program execution, clause 12**: "A full-expression is an expression that is not a subexpression of another expression. – Mohit Jain Apr 29 '14 at 14:04
  • `my_str` does not match its use - it takes a `const std::string&`, not a `const boost::format&`; and **that** is where the problem is. – Angew is no longer proud of SO Apr 29 '14 at 14:04
  • Yes, now it works. But it only saves the `.c_str()` suffix, not the full `.str().c_str()` as the OP wanted. – Angew is no longer proud of SO Apr 29 '14 at 14:09
  • OP's concern is _the string returned by str() is deallocated when the function returns, and no valid C string is returned_. But I understand your suggestion is right to use `const boost::format&` as constructor argument and later avoid `.str().c_str()`. – Mohit Jain Apr 29 '14 at 14:11
  • The OP knows that with `.str().c_str()` spelled out explicitly, it works (because they're both part of the same full-expression). The goal is to get rid of this suffix without creating a dangling reference. – Angew is no longer proud of SO Apr 29 '14 at 14:28