Compile time format string check

Richard (Rikki) Andrew Cattermole richard at cattermole.co.nz
Mon Mar 23 08:02:39 UTC 2026


On 23/03/2026 8:13 PM, Ada wrote:
> On Monday, 23 March 2026 at 05:46:15 UTC, Richard (Rikki) Andrew 
> Cattermole wrote:
>> On 23/03/2026 6:36 PM, Ada wrote:
>>> On Monday, 23 March 2026 at 05:33:05 UTC, Richard (Rikki) Andrew 
>>> Cattermole wrote:
>>>> On 23/03/2026 6:29 PM, Ada wrote:
>>>>> On Sunday, 22 March 2026 at 22:49:24 UTC, Richard (Rikki) Andrew 
>>>>> Cattermole wrote:
>>>>>> [...]
>>>>>
>>>>> I clearly said compile-time. maybe you misspoke.
>>>>> It's a bit of a nuisance because the function call then doesn't 
>>>>> resemble C that much anymore. (it's called betterc)
>>>>
>>>> The only way to do it at CT, is either with the pragma with a string 
>>>> literal for the format, or the format must be provided via a 
>>>> template parameter.
>>>>
>>>> You cannot do this, with a function parameter at CT.
>>>
>>> In that case, how would you do it with the pragma or the template 
>>> parameter?
>>
>> Start with the pragma, I linked the docs for it.
>>
>> Parsing out the format spec by itself isn't a fun job and is libc 
>> dependent.
> 
> Hmm, just to be clear. The pragma just says that the function marked 
> with it will follow the C99 Standard 7.19.6.1.
> Does that standard specify that custom format specifiers can be defined 
> by any chance?

No.

> Because that's essentially what I am trying to achieve.

Yeah you are a tad on your own on this one.

Here is one way to do it with recursive functions.

I had to create a indexOf util function as std.algorithm appears that it 
might've had a regression (WONTFIX) for betterC.

```d
size_t indexOf(string haystack, char needle) {
     foreach(i, c; haystack) {
         if (c == needle)
             return i;
     }

     return haystack.length;
}

void myPrint(string Format, Args...)(auto ref Args args) {
     import core.stdc.stdio : printf;

     size_t parseFormat(string text, size_t offset) {
         if (text[offset + 1] == 's' || text[offset + 1] == 'c')
             return 1;
         else
             assert(0, "invalid format");
     }

     void before(string text) {
         printf("%.*s", cast(int)text.length, text.ptr);
     }

     void actual(string Format3, T)(auto ref T value) {
         static if (is(T == string) && Format3[1] == 's') {
             printf("%.*s", cast(int)value.length, value.ptr);
         } else {
             printf(Format3.ptr, value);
         }
     }

     void handle(string Format2, size_t Arg)() {
         enum Offset = Format2.indexOf('%');
         enum Length = Format2.length > Offset ?
             (parseFormat(Format2, Offset) + 1) : 0;

         static if (Offset <= Format2.length) {
             if (Offset > 0)
                 before(Format2[0 .. Offset]);

             static if (Arg < Args.length)
                 actual!(Format2[Offset .. Offset + Length])(args[Arg]);

             static if (Format2.length > Offset + Length)
                 static if (Arg + 1 <= Args.length)
                     handle!(Format2[Offset + Length .. $], Arg + 1);
         } else {
             // no format???
             static assert(0, "Missing format for argument");
         }
     }

     handle!(Format, 0);
}

extern(C) {
     void main() {
         import core.stdc.stdio : printf;
         myPrint!"b%s%ce\n"("Hellorld", '!');
         printf("done\n");
     }
}
```



More information about the Digitalmars-d-learn mailing list