C++20, std::format, address of variables and Visual Studio

Pages: 12
Following on from here: http://www.cplusplus.com/forum/lounge/282147/ to https://stackoverflow.com/questions/70712797/is-it-safe-to-bind-an-unsigned-int-to-a-signed-int-reference (interesting discussion, BTW) I took the opening bit of code and decided to "update it" to use C++20 and std::format.

M'ok, works just fine. As expected.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import <iostream>;
import <format>;

int main()
{
   unsigned int u { 42 };

   const int& s   { static_cast<int>(u) };

   std::cout << std::format("u = {} s = {}\n", u, s);

   u = 6 * 9;

   std::cout << std::format("u = {} s = {}\n", u, s);
}

So....I decide to display the addresses of u and s. Easy to do using old school methods:
std::cout << "&u = 0x" << &u << " &s = 0x" << &s << '\n';

It does show the reference is indeed an unnamed temporary int, different address.

But....try to change that cout to use std::format and VS gets all whingey. VS doesn't apparently implement the p format. BOO!
std::cout << std::format("&u = {:p} &s = {:p}\n", &u, &s);

If only I could find a GCC version that has <format>, none of the versions I have installed have it.
Last edited on
For VS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <format>

int main() {
	unsigned int u {42};

	const int& s {static_cast<int>(u)};

	std::cout << std::format("u = {} s = {}\n", u, s);

	u = 6 * 9;

	std::cout << std::format("u = {} s = {}\n", u, s);
	std::cout << std::format("&u = {:p} &s = {:p}\n", (void*)&u, (void*)&s);
}



u = 42 s = 42
u = 54 s = 42
&u = 0x31f720 &s = 0x31f724


Note the cast for the pointers..
Last edited on
Doing a cast, and a C cast at that, is just so fooked......but this is MS, after all.

Using C++ casts is gonna be more than its usual bit cumbersomeness nature:
12
13
14
std::cout << std::format("&u = {:p} &s = {:p}\n",
                         reinterpret_cast<void*>(&u), 
                         reinterpret_cast<void*>(const_cast<int*>(&s)));

As yonked as that code looks, it does show how borked-up things are with this MS implementation of std::format to display addresses.

That's muh opinion, and I'm stickin' to it! Casting, PFFFFFFFFFFFFFFFT!

Just for 'freshers, the entire bit of modularized C++20 that does work:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import <iostream>;
import <format>;

int main()
{
   unsigned int u { 42 };

   const int& s { static_cast<int>(u) };

   std::cout << std::format("u = {} s = {}\n", u, s);

   std::cout << std::format("&u = {:p} &s = {:p}\n",
                            reinterpret_cast<void*>(&u), 
                            reinterpret_cast<void*>(const_cast<int*>(&s)));

   u = 6 * 9;

   std::cout << std::format("u = {} s = {}\n", u, s);
}

Yes, I know I could do #includes instead of imports, but for me it's a style thing. I can see at a glance this is C++20 (or later) code.

More and more I am really liking std::format. Displaying nicely formatted tables is ridiculously easy:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import <iostream>;
import <format>;

int main()
{
   unsigned int limit { };

   std::cout << "This program calculates n! and the sum of the integers "
             << "up to n for values 1 to limit.\n";
   std::cout << "What upper limit for n would you like? ";
   std::cin >> limit;

   // the format string for all rows of the table
   const auto table_format { "{:>8} {:>8} {:>20}\n" };

   // output column headings
   std::cout << std::format(table_format, "integer", "sum", "factorial");

   for (unsigned long long n { 1 }, sum {}, factorial { 1 }; n <= limit; ++n)
   {
      sum       += n;
      factorial *= n;

      std::cout << std::format(table_format, n, sum, factorial);
   }
}

Speaking of import vs. #include, several resources I've consulted/read mention importing the C++ library's C headers (<cstdio> for example) shouldn't work/be allowed. Well, with VS that just ain't true.

Importing <windows.h> appears to work as well.....
Displaying nicely formatted tables is ridiculously easy:


It's just a rip-off of python:
1
2
3
4
5
6
7
limit, sum, prod = 20, 0, 1
fmt = "{:>8} {:>8} {:>20}"
print( fmt.format( "n", "sum", "prod" ) )
for n in range( 1, limit + 1 ):
    sum  += n
    prod *= n
    print( fmt.format( n, sum, prod ) )


       n      sum                 prod
       1        1                    1
       2        3                    2
       3        6                    6
       4       10                   24
       5       15                  120
       6       21                  720
       7       28                 5040
       8       36                40320
       9       45               362880
      10       55              3628800
      11       66             39916800
      12       78            479001600
      13       91           6227020800
      14      105          87178291200
      15      120        1307674368000
      16      136       20922789888000
      17      153      355687428096000
      18      171     6402373705728000
      19      190   121645100408832000
      20      210  2432902008176640000

And Python in more than a few respects could be considered to be a badly malformed rip off of C/C++.

Cross fertilization of good ideas is never something to sneer at.
The standard format specification is based on the format specification in Python.

As always, C++ enables customisation for program defined types;
custom format specifications are at the discretion of the programmer.

For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <string_view>
#include <format>

struct rectangle { int height = 1 ; int width = 1 ; };

// a specialised formatter for type rectangle; this simple example reuses the standard formatter specialisation for std::string_view
// (this gives us the ability to enable all the format specifications that are supported by std::string_view)
template <> struct std::formatter<rectangle> : std::formatter<std::string_view>
{
    // use the inherited parse() from std::formatter<std::string_view>

    template < typename FORMAT_CONTEXT > // customise format() for rectangle
    auto format( const rectangle& r, FORMAT_CONTEXT& context )
    {
        return std::formatter<string_view>::format( std::format( "rect[{}x{}]", r.height, r.width ), context );
    }
};

int main()
{
    const rectangle rect { 20, 30 } ;
    // rect centred in a field of width 30, with the fill character as . 
    std::cout << '"' << std::format("{:.^30}", rect) << "\"\n" ; // ".........rect[20x30].........."
}
The solution worked for me thanks to the community and the members for the solution.

https://www.liteblue.app/
Last edited on
Note that std::format will throw if the format spec contains an error. It isn't forgiving! Can be a problem if the format spec is built via code and isn't hard-coded! We were converting some printf() and got a case wrong - boom!
The references on std::format I am currently studying from do indeed point out how easily it is to get the format "wrong" so it throws an exception. The resulting std::exception error(s) are not at all easy to decipher, I'd imagine even someone a lot more experienced than I would find them rather opaque and obtuse.

My attempt without the casting to printout memory addresses with std::format were really un-helpful.

@JLBorges, while I didn't know specifically the HOW to add custom format specifiers, thank you for showing me how, I did (somewhat) understand before it was possible since there are a lot of C++ standard library specializations listed for std::formatter.
https://en.cppreference.com/w/cpp/utility/format/formatter

Since I am still getting used to std::format, along with the rest of C++14/17/20, I doubt at this time I will ever see a need to create a custom format specialization.

Being a self-taught hobbyist I know the breadth and depth of my C++ knowledge to be small and shallow for all but the most common usages. *shrug*
Last edited on
@seeplus, according to cppreference the format specifier p doesn't need to be included when printing out an address, and sure enough it's not needed with VS:
1
2
3
4
5
6
7
8
9
import <iostream>;
import <format>;

int main()
{
   int x { };

   std::cout << std::format("x = {}, &x = {}\n", x, (void*) &x);
}
x = 0, &x = 0x403a12f850

The cast makes all the difference.
yeah - std::format will auto determine the type for output if not specified. I'm that used to using printf() (and snprintf() ) that I sort of 'auto pilot' convert to std::format. Producing a formatted string with std::format is so much easier than using snprintf() as you haven't got to deal with the c-string size. Yepee!
I will probably still use {:p} formatting so it is more obvious a pointer's address is being displayed, as required when using the C library format output functions.

Yeah, snprintf() is a more than a bit unwieldy using any of the C library format output functions in C++ code should be a thing of the past now that C++ has std::format. Proper type matching.
> Note that std::format will throw if the format spec contains an error.

std::vformat throws on invalid format strings (checked at run-time).

With std::format, an invalid format string would (should) result in a compile-time error (checked at compile-time).
A defect report corrected the originally specified behaviour: https://en.cppreference.com/w/cpp/utility/format/format#Defect_reports
A compile-time error/warning would be most helpful instead of the current situation of going *SPLAT!* at run-time as it does now with VS.

VS Intellisense isn't as up-to-date with C++20 as it should be as well, it still is experimental for a lot of features.
P2216R3 (format defect report) implementation is coming in VS 17.2. Considering that VS 17.1 is still in preview, that's likely to be a while...
Considering that I am still trying to wrap my brain around what C++20 added to the tool box (as well as other previous C++ features such as std::variant and std::valarray) I do believe I can wait. :)

The more I learn I about C++ I discover I actually know less and less. *sigh*
Well one of the complaints I hear about C++ standards is that they are too frequent! By the time the compilers are updated, books published, articles written, coders trained etc etc it's almost time for the next update. There's no time to consolidate on what you're got. One suggestion I've heard that I would support is to have a 2 year tick-tock cycle - similar to Intel. The tick would be a minor update (defective reports, having std::ssize(), z as a literal suffice etc) and tock would be the major update - and any minor required - (eg std::format, modules, ranges etc). This has the benefit of getting minor issues changed/incorporated more quickly but giving more time (4 years) between major changes for assimilation etc.
I know I certainly wouldn't complain with a bit more time to learn all the shi'ite the C++ standards committee keeps tossing out.

Even if the 2/4 year time frame was lengthened I still would find it hard to keep up learning on my own. Formal classroom training does have some advantages.

About C++ books....the older the standard the more books I can find available in printed form.

eBooks make writing books to newer standards like C++20 a bit easier, with some lag time between the approval of the standard and publication even if still "in progress." leanpub is a great resource for "still being written and edited" C++ books.

Whether the books/eBooks are worth a damn is still a major issue, a lot of trash is "out there."

C standards apparently took a much different, longer approach, there have been 3(?) official standards, starting with C99. Followed up by C11 and C17/C18.

C2x is expected next year.

Yes, there is the original K&R C, followed by ANSI C, but C99 is considered the starting point now for C standards.
Last edited on
seeplus wrote:
P2216R3 (format defect report) implementation is coming in VS 17.2.

I have the latest VS 2022 non-insider/non-preview revision, 17.0.6.

I hope MS doesn't break what is currently working when they repair defects and do upgrades.

*crosses 10 fingers and 10 toes*
one of the complaints I hear about C++ standards is that they are too frequent!

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1000r4.pdf - page 4

Why not every { one, two, four } years?
We find three years to be a good balance, and two years is the effective minimum in the ISO process.

The C++ IS schedule is a lot more wordy than the C IS schedule.

http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2864.pdf
Pages: 12