Fog Creek Software
Discussion Board




Dynamic formatting

I need to do something like this in c++.....

Imagine a statement

sprintf ( buffer, "%5d %20s",  intvalue, stringvalue) ;

I want to be able to change the format at run time say to :
"%20s %5d" and have the variables inserted properly. i.e the effect to be like:

sprintf ( buffer, "%20s %5d", stringvalue, intvalue) ;

For simplicity sake I can say that the number of values and the format type (integer/string/float) would be fixed and all the values are in their native format,

I know one way to do this would be to use vsprintf  and build the argument list individually but I would have to parse the variable names individually,

At the other end there are template libraries like eNitl  http://networkimprov.com/enit or Simkin http://www.simkin.co.uk/Docs/cpp/index.html but they seem to be overkill for doing something seemingly so simple.

Is there any other way this could be done more easily? Some simple scripting language? Some template processor? Some library? Some code?

Code Monkey
Monday, December 01, 2003

Lemme guess, you're internationalizing?

Alyosha`
Monday, December 01, 2003

Not really.

Just trying to move hardcoded formatting strings out of the code -- which is easily done but I wanted to go further and have the ability to change the format order too.

Code Monkey
Monday, December 01, 2003

It's not really clear to me why you need so much runtime flexibility.  However, you might want to avoid the printf family all together and std::stringstream:

std::string foo(int i, const std::string& s)
{
    std::stringstream ret;
    if (bar())
        ret << s << " " << i;
    else
        ret << i << " " << s;
    return ret.str();
}

Brian
Monday, December 01, 2003

One can never have enough run time flexibility :-)

Seriously though the reason is that the formats are not controlled by me and they change although usually too much..all I want to do is be able to accomodate minor changes e.g. changes in the length of a specific field or the order of the fields

I know of stringstream but I think it would be even more complicated to do...what with multiple if statements and formatting insertors like setw to be used....sprintf is good in that all the formatting info is part of a single string.

Thanks for the suggestion

Code Monkey
Monday, December 01, 2003

You may be able to dynamically build another string while keeping track of the argument list in an array and rearranging the array elements before passing it to sprintf.

// build szFormat

// sort array elements in desired order

sprintf ( buffer, szFormat, arrValue(0), arrValue(1) ) ;

...


Monday, December 01, 2003

how about

if (condition) sprintf ( buffer, "%5d %20s",  intvalue, stringvalue);
else sprintf ( buffer, "%20s %5d", stringvalue, intvalue);

Alex
Monday, December 01, 2003

>how about
>
>if (condition) sprintf ( buffer, "%5d %20s",  intvalue, >stringvalue);
>else sprintf ( buffer, "%20s %5d", stringvalue, intvalue);

Well that would work but imagine there are not just two but many such variables.

Infact in some places I have almost 8 such variables in a single sprintf statement!

Code Monkey
Monday, December 01, 2003

If you have access to regular expressions you could easily use a search and replace type method.  Heck, even without regular expressions you could roll your own light version of Java's MessageFormat class.

Jeff
Monday, December 01, 2003

Does this have to be pure C++, or will a Windows API do?

Check out the FormatMessage API - it works basically like sprintf, except that you can specify numeric indicators in your substitutions, so you can say "%2!s! %1!d!" (or something like that, it's been a while) and that'll give you the second argument as a string and the first as an int.

Plus it's got lots of i18n goodness too.

Chris Tavares
Monday, December 01, 2003

Take a peek at what your compiler does for va_list. It will probably be simple, if a bit tweaky. Then construct your own thing that looks like a va_list, and pass it to vsnprintf.

(Example: va_list on VC++7 is... "char *". Looks like you vould point it at the vararg objects in memory, one after the other, each aligned to an int-size boundary.)

There are potential portability problems, but I suspect they amount just to "this is not necessarily portable". I doubt there are any common compilers on common systems on which this would be impossible.

Alternatively, and portably, if you just want to change widths and not the order of the formatted objects, use the little-known * character. This replaces any number that is part of a format spec, and causes printf to extract an int from the varargs at that point and use that instead of the number in the string. For example:

int width=9;
printf("%-*d",width,value);

is the same as:

printf("%-9d",width);

Insert half smiley here.
Monday, December 01, 2003

That's wrong. But it would be correct to say that

    int width=9;
    printf("%-*d",width,value);

is the same as:

    printf("%-9d",value);

provided value is an int in both cases. And if you want the same output, it should obviously have the same value :)

Insert half smiley here.
Monday, December 01, 2003

I don't know about Windows, but on almost any Unix-like system, sprintf already has the feature you need:

sprintf(buffer, "%1$d %2$20s", intvalue, stringvalue);

sprintf(buffer, "%2$20s %1$d", intvalue, stringvalue);

You can read up on the %n$ syntax here:

http://www.opengroup.org/onlinepubs/007904975/functions/printf.html

Rob Mayoff
Monday, December 01, 2003

>That's wrong. But it would be correct to say that

>  int width=9;
>  printf("%-*d",width,value);

>is the same as:

>    printf("%-9d",value);

>provided value is an int in both cases. And if you want the >same output, it should obviously have the same value :)

Yes I am aware of this but I think this actually causes more problems in the situation I have. I think it is a nice feature that the formatting can be adjusted in the format string itself which allows one to see at one glance how the output is being formatted without having to look at the code or know the value of the "width" variable!

Thanks for the suggestion!

Code Monkey
Tuesday, December 02, 2003

"I have almost 8 such variables in a single sprintf statement!"

Why? (That is, why are they all in one sprintf.)


Tuesday, December 02, 2003

Try boost::format from

http://www.boost.org/

std::string fmt_string("%1% is %2%");
boost::format fmt(fmt_string);

fmt % numeric_value % string_value;
cout << fmt.str();

David Jones
Tuesday, December 02, 2003

>I have almost 8 such variables in a single sprintf statement!"

>Why? (That is, why are they all in one sprintf.)

They are in one sprintf because I am writing a fixed column based text file with a 8 columns

Code Monkey
Tuesday, December 02, 2003

>Try boost::format from
>
>http://www.boost.org/

>std::string fmt_string("%1% is %2%");
>boost::format fmt(fmt_string);

>fmt % numeric_value % string_value;
>cout << fmt.str();

That is very interesting and I will have a look at it.  Seems to have atleast some of the functionality I want which is to change the format specifier order and width without having to change the order of the variables in the code (which will need a recompile!)

Thanks for the suggestion!

Code Monkey
Tuesday, December 02, 2003

*  Recent Topics

*  Fog Creek Home