0

The following code compiles in clang but not in gcc:

#include <cstdarg>
#include <iostream>

struct Base {
  Base(const char* fmt, ...) __attribute__((format(printf, 2, 3))) {
    va_list ap;
    va_start(ap, fmt);
    vprintf(fmt, ap);
    va_end(ap);
  }
};

struct Derived : public Base {
  using Base::Base;
};

int main() {
  Derived d("%d", 3);
  return 0;
}

gcc complains:

<source>: In function 'int main()':
<source>:18:20: sorry, unimplemented: passing arguments to ellipsis of inherited constructor 'Derived::Derived(const char*, ...) [inherited from Base]'
   18 |   Derived d("%d", 3);
      |                    ^
<source>:14:15: note: declared here
   14 |   using Base::Base;
      |               ^~~~
Compiler returned: 1

How can I salvage it to work with gcc?

A similar question is asked here, but that question does not have format-string-validation requirements. As such, the first answer does not seem applicable, while the second answer appears to demand a copy+paste of the contents of the base class's constructor.

The above code is a simple toy example. In my actual use-case, my base-class inherits from std::exception, and allows me to conveniently construct an error message via printf-formatting and pass that message to an exception-constructor with a one-liner. I am open to any other approach that would allow me to define such a base-class along with derived-classes, all with compile-time printf-format-string-validation.

dshin
  • 2,354
  • 19
  • 29
  • What is the error? Just use variadic templates. – 273K Apr 12 '23 at 19:36
  • Why don't you try `std::format` with variadic templates? It requires C++20. But it is standard. You can output results through `std::puts`. – Red.Wave Apr 12 '23 at 19:39
  • @273K I linked to the godbolt page showing the error. Here it is: https://godbolt.org/z/KKcWMv6rr – dshin Apr 12 '23 at 19:43
  • No external links. The question should contain all relevant information. – 273K Apr 12 '23 at 19:44
  • @273K Made the change you requested. – dshin Apr 12 '23 at 19:48
  • @273K "Just use variadic templates" -> can you show an example? Particularly one that demonstrates the compiler raising an error if you try to use an invalid format string (e.g., "%s" with int). – dshin Apr 12 '23 at 19:55
  • @Red.Wave `std::format` looks like it is what I should want. But is it available in gcc? According to this [link](https://stackoverflow.com/q/65083544/543913), it is missing in gcc-12 and was only implemented in gcc-13. But gcc-13 does not appear to be publicly available yet? – dshin Apr 12 '23 at 20:01
  • You don't need `std::format`, it's a regular variadic template function. `template Base(Args&&... args) { ((std::cout << args), ...); } ` – 273K Apr 12 '23 at 20:23
  • I don't see `%s` in the example code you showed. I want to use %-specifiers, not `<<`. – dshin Apr 12 '23 at 20:25
  • As I mentioned, you need C++20. So the compiler must support and you have to set the compiler switch. You can also use the `fmt` opensource lib instead. But again you'll use variadic templates instead of C variadics. – Red.Wave Apr 12 '23 at 20:33
  • @Red.Wave I am using c++20. But as I mentioned, it appears that the latest g++ does not support `std::format`, as of April 2023. With that said, upon further investigation, it appears that `std::format` expects `{}` formatters, rather than `%` formatters, so even if it was available, adopting it would require significant work. – dshin Apr 12 '23 at 20:38
  • That means your version of compiler doesn't support C++20; you can use `fmt` lib instead. The `{}` is the future of formatting. Other options are more costly in terms development efforts, and more error-prone. IMO adapt to the new format and use `fmt`. When you manege to update the compiler, switching to `std::format` won't be too much of a problem. – Red.Wave Apr 12 '23 at 20:49
  • If you want using `%d`, add `Base(const char* fmt, va_list vlist)` overloaded constructor. – 273K Apr 12 '23 at 20:52
  • @Red.Wave Please check this [link](https://en.cppreference.com/w/cpp/compiler_support), and search for [P0645R10](https://wg21.link/P0645R10). You will see that gcc-12 supports nearly all of c++20, but `std::format` is one of the few features that it does not support. gcc-13 does support P0645R10, but it has not yet been released (AFAIK). – dshin Apr 12 '23 at 20:57
  • @273K If you want to put that into an answer, I'd be happy to accept it. If you could include details showing how to invoke that constructor from the C-variadic constructor, that would much appreciated, as it's not obvious to me how to do so. – dshin Apr 12 '23 at 21:05
  • It seems impossible with the constructor. What about [this approach](https://godbolt.org/z/vh97nhrrh)? – 273K Apr 12 '23 at 21:15
  • Sure, that works. It's inelegant that each derived class needs so much repeated code. With clang, as I demonstrated, we can do what I want elegantly, but alas perhaps this is the best we can do in gcc. I may resort to macros. I should note that my actual base class does not use `vprintf()`, but rather `vsnprintf()` to a char buffer with dynamic-resizing in case of overflow. I just used `vprintf()` here for the sake of a minimal example, which I why I remarked that this is a toy-example. Adapting your approach to my actual usage is straightforward. – dshin Apr 12 '23 at 21:25
  • `fmt` should work. Just mind not to use parts that `std::format` doesn't support. – Red.Wave Apr 12 '23 at 22:07

0 Answers0