Operator...

M

martyn_dobson

this has probably been asked/suggested many timtes before, but i just
thought of it.

why is there no such thing as something like operator...()
imho it would allow us to smooth off one of the sharper corners of c+
+.

you could maybe do something like this:

class string
{
public:

const char *operator...() const
{
return c_str();
}
};


void foo(const char *format, ...)
{
//the usual vaarg stuff
}


void bar()
{
string s = "test";

foo("this is a test %s", s);
}



just a quick thought,

M.
 
M

martyn_dobson

yeah the standard thing is to have to call s.c_str().
the standards board purposefully didnt put an operator char* into
string for the reason that you cant make the std::string class act
entirely like a pod type like in other languages because of variable
arguments.

i meant this only as a possible conversion for a class beign passed
into a variable argument parameter list. its one of those things that
catches people out, and makes other programmers wonder why c++ isnt
more helpfull some times.

just thought it could be a handy little addition.

M.
 
J

Juha Nieminen

this has probably been asked/suggested many timtes before, but i just
thought of it.

why is there no such thing as something like operator...()
imho it would allow us to smooth off one of the sharper corners of c+
+.

The next C++ standard will introduce variadic templates, which will
allow writing type-safe functions which take a variable amount of
parameters. I believe that will allow you to do what you are referring
to here.
 
M

Maxim Yegorushkin

this has probably been asked/suggested many timtes before, but i just
thought of it.

why is there no such thing as something like operator...()
imho it would allow us to smooth off one of the sharper corners of c+
+.

you could maybe do something like this:

class string
{
public:

        const char *operator...() const
        {
                return c_str();
        }
};

void foo(const char *format, ...)
{
        //the usual vaarg stuff

}

void bar()
{
        string s = "test";
        foo("this is a test %s", s);
}

I quite like format strings myself because of its concise syntax.
However, C++ objects do not like to be passed into "...".

The shortest syntax for passing C++ objects into "..." along with
formatting them into automatically allocated buffers is prefixing
objects with an overloaded operator~() (binary negation). This
overloaded operator allocates a temporary buffer on the stack, formats
an object into that buffer and returns an argument for "%.*s" format
string. Obviously, the buffer is only alive till until the full
expression has been evaluated.

Please scroll down to main() first to see usage:

#include <stdio.h>

// hack: supposed to be layout compatible with arguments for
"%.*s"
struct StarString
{
int n;
char* p;
};

struct TmpBuf
{
char buf[0x100];
StarString format_arg;

template<class T>
TmpBuf(T const& t)
{
format_arg.n = sizeof buf;
format_arg.p = buf;
format_arg = t.format(format_arg);
};
};

StarString operator~(TmpBuf const& buf)
{
return buf.format_arg;
}

struct Y
{
StarString format(StarString s) const
{
s.p = "Hello world from Y";
s.n = sizeof "Hello world from Y" - 1;
return s;
}
};

struct X
{
StarString format(StarString s) const
{
s.n = snprintf(s.p, s.n, "X@%p", this);
return s;
}
};

int main()
{
// what ~Y() does:
// 1) creates a temporary Y object
// 2) creates a temporary TmpBuf object
// 3) calls Y::format() to format the Y object into the
buffer provided by TmpBuf
// 4) return StarString which is passed into %.*s instead of
(int, char*).
printf("%.*s\n", ~Y());
printf("%.*s\n", ~X());
}
 
I

Ioannis Vranos

Maxim said:
[...]
>
struct X
{
StarString format(StarString s) const
{
s.n = snprintf(s.p, s.n, "X@%p", this);


There isn't any snprintf in C++98/03.
 
J

Juha Nieminen

Maxim said:
I quite like format strings myself because of its concise syntax.

The major problem with printf-style format strings is that they are
not very abstract. This means that it's very hard to abstract away some
type because the format string needs to know the exact type, as a string
(rather than as an overloaded function or whatever).

I think this example illustrates perfectly the problem with format
strings:

template<typename T>
void foo(const T& value)
{
std::cout << value << "\n"; // ok

std::printf("%???\n", value); // What to put here???
}

Of course templated functions are not the only situation where the
lack of abstraction of format strings can hit you hard. It's enough to
have typedeffed some type, which you later want to print. (Sure, you can
try to struggle by also #defining the format string for that type, but
that's awkward and has a multitude of problems in itself.)

If I'm not mistaken, the next C++ standard will introduce a feature
which will allow an abstract and type-safe way of giving a variable
amount of parameters to a function, with the so-called variadic templates.
 
M

Maxim Yegorushkin

  The major problem with printf-style format strings is that they are
not very abstract. This means that it's very hard to abstract away some
type because the format string needs to know the exact type, as a string
(rather than as an overloaded function or whatever).

  I think this example illustrates perfectly the problem with format
strings:

template<typename T>
void foo(const T& value)
{
    std::cout << value << "\n"; // ok
    std::printf("%???\n", value); // What to put here???
}

You put %s in there because it does not matter what you are
outputting, what does matter is that you want to have a textual form
of it.

The real problem is that C++ does not have a native format mechanism.
std::eek:stream is just a type safety hack over snprintf() and
boost::format is another hack over std::eek:stream. In other words, all
roads lead back into snprintf().

My dream high-performance C++ formatting mechanism would represent the
output sequence as an iterator and formatting would emit symbols
directly into the iterator.
 
J

Juha Nieminen

Maxim said:
The real problem is that C++ does not have a native format mechanism.
std::eek:stream is just a type safety hack over snprintf()

I disagree. C++ streams are not so much about type safety (well, they
are about that too, but that's not the *only* thing), but about abstraction.

The C printf family of function are the hack, not the C++ streams.
When they designed C they had to invent *some* way of printing different
types in a way which would be as easy to use as possible. The solution
was support for variable amount of parameters to a function (which works
because C was built on stack-based architectures where such a thing is
feasible) and format strings. Then C compilers spent the next 20+ years
optimizing the runtime format string interpreters to be as fast as
possible (which they succeeded in doing pretty well in the end).

However, that doesn't make the printf functions and format strings
less of a hack, because they are just that. They are completely
non-abstract, error-prone and type-unsafe.

C++ streams are not a hack over printf. C++ streams are a
*replacement* of printf. A replacement which is intended to be
everything that the printf functions aren't, ie. abstract, type-safe and
less error-prone. They allow you to write code which safely uses streams
without having to know anything about the types being handled.

Sure, C++ streams might not be as easy to use as could be possible,
but they are definitely a step to the right direction.
and
boost::format is another hack over std::eek:stream. In other words, all
roads lead back into snprintf().

Wrong, all roads lead *away* from the printf family of functions,
because *those* are the real hacks, and while they may work in C, they
definitely don't work in C++. Not if you need even the slightest amount
of abstraction.
 
J

James Kanze

You put %s in there because it does not matter what you are
outputting, what does matter is that you want to have a
textual form of it.

Except that it likely does matter.

Part of the problem is that in most programs, a single type
(e.g. double, or int) is used for values with radically
different semantics (at the user level). You want to specify
the formatting for each set of semantics program wide, however,
and not in each print statement. With iostream's, of course,
you define a manipulator for each semantics, and the job is
done. Very few other languages I've seen offer this
possibility, however, which means that you end up embedding such
critical information deep within the code at many different
places. (In well written C++, you'll almost never see any of
the standard manipulators, except perhaps std::setw. Almost
every value output will be preceded by some application specific
manipulator, however.)

In the end, the printf format specifiers are about the same as
integral or floating point literals. You don't want either in
your code.
The real problem is that C++ does not have a native format
mechanism.

Obviously, since formatting is independent of the language. As
I said above, it depends on the high level semantics of the
value. What would the native format of a double be? It just
doesn't make sense.

More generally, "formatting" is really a case where you need
double dispatch, on both the type being output, and the type of
stream it's being output to. You don't want to overcharge data
types with knowledge of how they are formatted (for twenty
different types of streams, binary and text). But of course,
the stream itself shouldn't even know of the existance of your
types.

In this regard, the solution used in iostream is pretty clever;
it leverages off the fact that the type being output doesn't
really need to be dynamically resolved, and uses overloading for
that.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,043
Latest member
CannalabsCBDReview

Latest Threads

Top