Formatted static_assert and nameof

2,223 views
Skip to first unread message

Chris Gary

unread,
Feb 19, 2015, 12:33:33 PM2/19/15
to std-pr...@isocpp.org
This has been posted somewhere before, asked about before in other places, thought about and not attempted (AFAIK)... Can't find it here though....

Some code:

template<typename CantBeWeirdType>
class complicated
{
   
static_assert(
       
! is_weird<CantBeWeirdType>::value,

       
"Can't create a complicated<%[0]>, %[0] is weird!\n"
       
"Also %0 is %1 bytes in size, which is probably bad!\n"
       
"The alignment of %[0] is 0x%xp[2], which is not supported!",

       
// Argument 0: Plain string literal (unqualified name)
        nameof
(CantBeWeirdType),
       
       
// Argument 1: Lossless string literal in base-10 by default
       
sizeof(CantBeWeirdType)
       
       
// Argument 2: Lossless string literal in base-16 (%x)
       
// E.G: ABCD (no 0x)
       
alignof(CantBeWeirdType)
       
);

   
template<typename ArgumentType>
   
void f(ArgumentType &&x)
   
{        
       
static_assert(
           
! is_weird<decay<ArgumentType>>::value,
           
"Argument %[0] is of a weird type!\n",
           
           
// Unqualified name, (reduces to "x", but using symbols helps refactoring tools...)
            nameof
(x)
           
);
   
}
};


When complicated<> is given a weird_thing:

Error <line><file>: Can't create a complicated<weird_thing>, weird_thing is weird!
                    Also, weird_thing is 42 bytes in size, which is probably bad!
                    The alignment of weird_thing is 0x0000DC81, which is not acceptable!

Proposal:

    //nameof, qnameof, and dnameof
   
   
namespace space_of_names{
       
struct type{};
   
}
   
   
// Expands to string literal "type"
   
auto name_str = nameof(space_of_names::type);
   
   
// Expands to string literal "space_of_names::type"
   
auto qname_str = qnameof(space_of_names::type);
   
   
// Expands to string literal ".?QUtype@space_of_names@@" (through MSVC)
   
auto dname_str = dnameof(space_of_names::type);
   
   
// And similarly for variable names.


static_assert format specifiers:

    %<style-opt><width-opt><[base-10 arg index]>
   
   
<style> is one of:
   
   
'' <- Decimal (nil)
   
'x' <- Hexadecimal
   
'o' <- Octal
   
'b' <- Binary

   
<width> can be a base-10 number of spaces to pad up to
   
or 'p' for 'width of pointer'. Integers are 0-padded,
    strings are space
-padded.

If an index is out of range, the output prints "ERANGE" in place of the argument.

Incomplete? Done already? Useful? Not useful? Too hard? Too easy? Niche? Sufficiently eclipsed by concepts?

I've lost track of how many times a simple type or variable name could clarify a static_assert error...

Ville Voutilainen

unread,
Feb 19, 2015, 12:39:09 PM2/19/15
to std-pr...@isocpp.org
On 19 February 2015 at 19:33, Chris Gary <cgar...@gmail.com> wrote:
> Incomplete? Done already? Useful? Not useful? Too hard? Too easy? Niche?
> Sufficiently eclipsed by concepts?

Seems like SG7 material, a combination of reflection and compile-time strings.
Somewhat hard, somewhat niche, not useful enough imho to put all of that
functionality into static assert. Most of the necessary pieces might
become available
in the future and then you could combine them generally.

Nevin Liber

unread,
Feb 19, 2015, 1:40:30 PM2/19/15
to std-pr...@isocpp.org
I just wonder if there is some middle ground.

After all, if you do:

static_assert(sizeof(int) == 4);


and it fires, it is useful to see what sizeof(int) is in the message.

Right now when I need this kind of thing, I use undefined template specializations so that the number I'm trying to output shows up cryptically in the error message:


template<typename T, T v>

struct Expected : std::integral_constant<T, v> {};


template<typename T, T v>

struct Actual : std::integral_constant<T, v> {};


template<typename E, typename A>

struct AssertEqual;


template<template<typename LL, LL> class L, template<typename RR, RR> class R, typename T, T v>

struct AssertEqual<L<T, v>, R<T, v>> : std::true_type {};


static_assert(AssertEqual<Expected<size_t, 4>, Actual<size_t, sizeof(int)>>(), "UGH");



We should be able to do better, not that I know what such a proposal would look like...
--
 Nevin ":-)" Liber  <mailto:ne...@eviloverlord.com(847) 691-1404

Chris Gary

unread,
Feb 21, 2015, 8:36:05 AM2/21/15
to std-pr...@isocpp.org

*snip*

We should be able to do better, not that I know what such a proposal would look like...

These type-safe string formatting APIs are so easy to write its almost funny.

The only reason printf, ostream, et. al. are a PITA to implement is due to their type-awareness: printf is a "god method" and "<<" with ostream needs to be overridden for each and every printable type, creating a kludgey dependency on IO.

We could require all arguments to static_assert to be strings, and just provide a way, at compile time, to create formatted strings out of constants.

Even better, why not just one new utility that just formats strings, and just use that inside of static_assert:

class coconuts{};

coconuts bunch{};

static const size_t count = 55;


// Inherits the width of the format string.
//
// vnameof() applies only to variables:
// Probably has other options, just makes a lossless base-10 string literal by default.
//
const auto *str = static_format("I've got a lovely %0 of %2 %1", nameof(coconuts), nameof(bunch), vnameof(count));

Hardly complete, but the idea is that format specifiers would be replaced instead by argument specifiers for the sake of type agnosticism.

As opposed to stringifying through the preprocessor, you get type names and formatted values.

David Krauss

unread,
Feb 21, 2015, 9:05:56 AM2/21/15
to std-pr...@isocpp.org
On 2015–02–20, at 1:39 AM, Ville Voutilainen <ville.vo...@gmail.com> wrote:

Seems like SG7 material, a combination of reflection and compile-time strings.
Somewhat hard, somewhat niche, not useful enough imho to put all of that
functionality into static assert. Most of the necessary pieces might
become available
in the future and then you could combine them generally.

The only hard part is treating it like a string, which is also totally unnecessary.

We already have all we need. The compiler is able to print diagnostics with types and values. Just give static_assert a sequence of typenames, template-names, and values, with string literals being one kind of value. It should do its best to present them all nicely to the user.

Any syntactic ambiguity between those categories can be resolved at the implementation’s discretion, and it should also endeavor to soldier on if a failure interrupts computation of part of the diagnostic. Diagnostic messages are fundamentally different from exact metaprocessing results, because they only exist to convey information about some failure, so it’s not appropriate to apply strict constraints while generating them.

If we do get “compile-time printf” in the future, formatted constexpr or variadic template strings are also just another kind of data to throw into the pretty-printer.

Ville Voutilainen

unread,
Feb 21, 2015, 9:42:41 AM2/21/15
to std-pr...@isocpp.org
On 21 February 2015 at 16:05, David Krauss <pot...@gmail.com> wrote:
>
> On 2015–02–20, at 1:39 AM, Ville Voutilainen <ville.vo...@gmail.com>
> wrote:
>
> Seems like SG7 material, a combination of reflection and compile-time
> strings.
> Somewhat hard, somewhat niche, not useful enough imho to put all of that
> functionality into static assert. Most of the necessary pieces might
> become available
> in the future and then you could combine them generally.
>
>
> The only hard part is treating it like a string, which is also totally
> unnecessary.

I see it as a much superior way forward than just enhancing static_assert.

> We already have all we need.

Oh, ok, end of discussion, then.

David Krauss

unread,
Feb 21, 2015, 10:41:41 AM2/21/15
to std-pr...@isocpp.org

> On 2015–02–21, at 10:42 PM, Ville Voutilainen <ville.vo...@gmail.com> wrote:
>
> I see it as a much superior way forward than just enhancing static_assert.

String processing is nice to have in general, but relying on it in this case will sacrifice usability.

>> We already have all we need.
>
> Oh, ok, end of discussion, then.

We currently have all that’s needed for the 99% of users who won’t be meticulously formatting strings, which is simply to specify a pretty-printer.

If you read to the end of my message, you’ll see that this still doesn’t prejudice us against the other 1%. But, it’s up to you what you want to read, and what you want to skip with such prejudice of your own.

sasho648

unread,
Feb 21, 2015, 11:47:01 AM2/21/15
to std-pr...@isocpp.org
I'm really happy that recently people have found some really nice examples of when meta-programming is needed. I think that 'constexpr' idea should be extended and thus enabling meta-code not only in templates but also in normal functions and possibly even in classes. I have written this in a suggestion here.

In order your idea to be made the right way - I first suggest removing 'static_assert' as a C++ keyword and re-implementing it as a standard C++ 'constexpr' function which takes a 'constexpr' parameter of type boolean and another optional one of type pointer to constant char (supposing that 'constexpr' parameters are included and you've read my proposal) :

constexpr void static_assert(constexpr bool bool_constexpr, constexpr const char *constexpr pmessage = nullptr) ;

This is a little more advanced example of my suggestion as it pretends that 'constexpr' variables can be actually edited. I think this is a good idea but for simplicity you could just thought it like:

constexpr void static_assert(constexpr bool bool_constexpr, constexpr char *constexpr pmessage = nullptr) ;

Another different part is that there is a 'constexpr' pointer. I suggest that there will be such thing which will point to some place which can be told by the compiler without running the program. I could give some examples like:

int var;

int *constexpr ptr_var = &var; //here 'ptr_var' is an address which can be represented by the compiler as '&var'

int *ptr_var_n = new int; //here 'ptr_var_n' address can't be represented by the compiler as it's determined during run-time by an external non-constexpr function call

And also a pointer pointing to 'constexpr' data is something new but it is not so different to explain - it'll be just a pointer addressing internal compiler memory. Such pointer can be only constexpr by itself because run-time environment have no access to compiler-internal memory.

So a possible implementation of your idea could be as simple as a different version of 'static_assert':

constexpr void static_assert(constexpr bool bool_constexpr, constexpr const char *constexpr pstrformat, ...) ;

If we use variable parameters.

I think the best solution yet for all meta-programming problems are my 'constexpr' changes which could apply for another problem which is including files as a constexpr variables.

Ville Voutilainen

unread,
Feb 21, 2015, 12:05:55 PM2/21/15
to std-pr...@isocpp.org
On 21 February 2015 at 17:41, David Krauss <pot...@gmail.com> wrote:
> We currently have all that’s needed for the 99% of users who won’t be meticulously formatting strings, which is simply to specify a pretty-printer.

Then by all means let's specify a compile-time printer. Specifying a
static_assert-specific pretty
printer doesn't make much sense. While we are specifying such a
compile-time printer,
we will probably want compile-time string-processing in general, since
it's useful for far
more than just printing diagnostics.

David Krauss

unread,
Feb 21, 2015, 12:59:44 PM2/21/15
to std-pr...@isocpp.org
On 2015–02–22, at 1:05 AM, Ville Voutilainen <ville.vo...@gmail.com> wrote:

On 21 February 2015 at 17:41, David Krauss <pot...@gmail.com> wrote:
We currently have all that’s needed for the 99% of users who won’t be meticulously formatting strings, which is simply to specify a pretty-printer.

Then by all means let's specify a compile-time printer. Specifying a
static_assert-specific pretty
printer doesn't make much sense.

Perhaps I wasn’t clear. I’m not suggesting to specify the behavior of a pretty printer which is specific to static_assert. I’m suggesting to specify an interface to the fault-tolerant pretty-printers that all implementations already have for ordinary diagnostics.

Grammar:

static_assert-declaration:
    static_assert( constant-expression  message-seq_opt )
message-seq:
    message-seq_opt , message
message:
    expression
    type-id
    template-name

Standardese-ish (sorry, it’s getting late here):

If the constant-expression evaluates to false, the program is ill-formed, and the implementation shall communicate the identity or value of each message, if any. Messages which are string literals, or otherwise evaluate to strings, should be relayed as plain text. If the value or identity of a message cannot be determined, the implementation should convey whatever information may be useful, and then proceed to communicate the remaining messages.

Currently, static_assert diagnostic messages are required to “include the text of the string-literal, if one is supplied, except that characters not in the basic source character set (2.3) are not required to appear in the diagnostic message.” This is litigious, yet still underspecified: GCC and Clang vary on whether escape sequences are supposed to be translated or relayed.

Vague specification and implementation freedom are the way to best serve the user. I want a development environment with interactive, point-and-click diagnostic messages. That’s possible if they’re generated natively, not through reflection. (But, as I said, this doesn’t preclude reflection-based strings if they’re available.)

While we are specifying such a
compile-time printer,
we will probably want compile-time string-processing in general, since
it's useful for far
more than just printing diagnostics.

Since diagnostic messages are likely to try to expose errors, it’s likely that the thing being formatted isn’t completely OK, which will cause string processing to fail and no message comes out.

Fault tolerance is impossible to add through reflection.

Thiago Macieira

unread,
Feb 21, 2015, 1:46:59 PM2/21/15
to std-pr...@isocpp.org
On Saturday 21 February 2015 19:05:53 Ville Voutilainen wrote:
> we will probably want compile-time string-processing in general, since
> it's useful for far
> more than just printing diagnostics.

That ties to the #load discussion going on.

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358

sasho648

unread,
Feb 21, 2015, 2:04:21 PM2/21/15
to std-pr...@isocpp.org
This and I think it will be better to implement it using currently defined primitives instead of adding new keywords and libraries. Thus I'm suggesting a 'constexpr' version of the current standard library which to operate on compiler-time data. 
Reply all
Reply to author
Forward
0 new messages