For various reasons I am looking for a way to capture the constexpr-ness of arguments passed to a function. It's a bit tricky to explain so I think code best demonstrates what I like to achieve
#include <vector> // For std::size_t
#include <cstdio>
namespace
{
template<std::size_t N, typename ...TArgs>
constexpr int cstrlen (char const (&s) [N], std::size_t i = 0) noexcept
{
return i < N && s[i] != 0
? 1 + cstrlen (s, i + 1)
: 0
;
}
template<std::size_t N, typename ...TArgs>
inline void silly_printf (char const (&format) [N], TArgs && ...args) noexcept
{
static_assert (cstrlen (format) > 0, "format must not be empty string");
printf (format, std::forward<TArgs> (args)...);
}
}
#define SILLY_PRINTF(format, ...) \
static_assert (cstrlen (format) > 0, "format must not be empty string"); \
printf (format, ##__VA_ARGS__);
int main()
{
// This works but relies on macros
SILLY_PRINTF ("Hello: %d", 1);
// This doesn't work
silly_printf ("Hello: %d", 1);
return 0;
}
I can't get silly_printf to work as I want it to. The compiler complains that the expression doesn't evaluate to a constant. We know it's constexpr when calling silly_print with a string literal but the constexpr-ness gets lost (I am using VS2015 here by the way).
I was thinking perhaps I can add constexpr to parameters (much like const) but no success there.
I can work-around this using macros (demonstrated by SILLY_PRINTF macro) but that feels like a failure.
Any ideas most welcome.
PS. What I am really is trying to achieve is slightly less silly
There's a GNU extension (supported by g++ and clang) that allows user defined literals of the form:
template<typename CharT, CharT... Chars>
constexpr void operator"" _something() { }
With this, one can build a constexpr-string type without macros that could be used like this:
constexpr auto str = "testing\0length"_string;
static_assert(str.strlen() == 7, "!");
By encoding all the string's properties into the type, you can then static_assert on it anywhere, constexpr or not.
For example, in your silly_printf:
template<typename CharT, CharT... Chars, typename... Args>
void silly_printf(const constexpr_string<CharT, Chars...>& format_string, Args&&... args) {
static_assert(format_string.strlen() > 0, "format string must not be empty");
printf(format_string.c_str(), args...);
}
And use it like this:
silly_printf("testing %d %s %x embedded\0null"_string, 1, "2", nullptr);
You could also use another operator"" _silly_printf() returning a function object to get a syntax like "format string"_silly_printf(args...).
See it live on Coliru
You don't need to use a char array reference as the argument. Here is one that I use but you need to have c++14 relaxed constexpr rules:
using size_t=decltype(sizeof(int));
namespace util
{
template<typename char_t>
constexpr size_t str_size(const char_t*)noexcept;
}
template
<typename char_t>
constexpr auto
util::
str_size
(const char_t* const a_str)noexcept->size_t
{
const char_t* a_char=a_str;
while(*a_char!=char_t(0))
{
++a_char;
}
return size_t(a_char-a_str);
}
static_assert(util::str_size("hello")==size_t(5),"");
If you can't use c++14 a recursive version will work too. You still just use a char pointer as the argument rather than a char array reference.
Related
I have two broadly related questions.
I want to make a function that forwards the arguments to fmt::format (and later to std::format, when the support increases). Something like this:
#include <iostream>
#include <fmt/core.h>
constexpr auto my_print(auto&& fmt, auto&&... args) {
// Error here!
// ~~~~~~~~v~~~~~~~~
return fmt::format(fmt, args...);
}
int main() {
std::cout << my_print("{}", 42) << std::endl;
}
Tested with gcc 11.1.0:
In instantiation of ‘constexpr auto my_print(auto:11&&, auto:12&& ...) [with auto:11 = const char (&)[3]; auto:12 = {int}]’:
error: ‘fmt’ is not a constant expression
And tested with clang 12.0.1:
error: call to consteval function 'fmt::basic_format_string<char, int &>::basic_format_string<char [3], 0>' is not a constant expression
In the library (core.h) it's declared something like this:
template <typename... T>
auto format(format_string<T...> fmt, T&&... args) -> std::string {
// ...
}
The problem is that cppreference indicates that the type of the first parameter is unspecified. So
How can I make a function like my_print that passes the arguments to fmt::format and still catches the same kind of errors? Is there a more general way to do this for any kind of function?
How can I infer the type of a parameter of a function like std::format?
For more context, I want to make a function that calls to std::format conditionally, avoiding the formatting at all if the string won't be needed. If you know a better way to make this leave a comment, I'll be very greatful. However, my question about how to solve the general problem still stands.
C++23 may include https://wg21.link/P2508R1, which will expose the format-string type used by std::format. This corresponds to the fmt::format_string type provided in libfmt. Example use might be:
template <typename... Args>
auto my_print(std::format_string<Args...> fmt, Args&&... args) {
return std::format(fmt, std::forward<Args>(args)...);
}
Before C++23, you can use std::vformat / fmt::vformat instead.
template <typename... Args>
auto my_print(std::string_view fmt, Args&&... args) {
return std::vformat(fmt, std::make_format_args(std::forward<Args>(args)...));
}
https://godbolt.org/z/5YnY11vE4
The issue is that std::format (and the latest version of fmt::format) require a constant expression for the first parameter, as you have noticed. This is so that it can provide compile-time errors if the format string does not make sense for the passed-in arguments. Using vformat is the way to get around this.
Obviously this sidesteps the compile-time checking normally done for a format string: any errors with the format string will manifest as runtime errors (exceptions) instead.
I'm not sure if there's any easy way to circumvent this, apart from providing the format string as a template parameter. One attempt may be something like this:
template <std::size_t N>
struct static_string {
char str[N] {};
constexpr static_string(const char (&s)[N]) {
std::ranges::copy(s, str);
}
};
template <static_string fmt, typename... Args>
auto my_print(Args&&... args) {
return std::format(fmt.str, std::forward<Args>(args)...);
}
// used like
my_print<"string: {}">(42);
https://godbolt.org/z/5GW16Eac1
If you really want to pass the parameter using "normal-ish" syntax, you could use a user-defined literal to construct a type that stores the string at compile time:
template <std::size_t N>
struct static_string {
char str[N] {};
constexpr static_string(const char (&s)[N]) {
std::ranges::copy(s, str);
}
};
template <static_string s>
struct format_string {
static constexpr const char* string = s.str;
};
template <static_string s>
constexpr auto operator""_fmt() {
return format_string<s>{};
}
template <typename F, typename... Args>
auto my_print(F, Args&&... args) {
return std::format(F::string, std::forward<Args>(args)...);
}
// used like
my_print("string: {}"_fmt, 42);
https://godbolt.org/z/dx1TGdcM9
It's the call to the constructor of fmt::format_string that needs to be a constant expression, so your function should take the format string as a fmt::format_string instead of a generic type:
template <typename... Args>
std::string my_print(fmt::format_string<Args...> s, Args&&... args)
{
return fmt::format(s, std::forward<Args>(args)...);
}
NOTE: This is MSVC, C++17 question.
Disclaimer: I know this has been attempted, and yes I was trying to find a relevant SO answer.
I can code the UDL, to achieve transforming numeric literals to std::array, at compile time:
// std::array{ '1','2','3' }
constexpr auto a_1 = 123_std_char_array;
// std::array{ '0','x','1','2' }
constexpr auto a_2 = 0x12_std_char_array;
// std::array{ '4'.'2','.','1','3' }
constexpr auto a_3 = 42.13_std_char_array;
And this is the UDL, I made:
template< char ... Chs >
inline constexpr decltype(auto) operator"" _std_char_array( )
{
// append '\0'
return std::array { Chs..., char(0) } ;
}
Amazing, snazzy, modern, blah,blah,blah ... But.
The Question
How do I code an UDL to allow for this:
// std::array {'S','t','r','i','n','g'}
constexpr auto std_char_array_buff_ =
"String"_std_char_array ;
In MSVC, C++17, please.
The Confession
I know UDL to "catch" the string literal has to have this footprint:
inline auto operator"" _X( const char*, size_t);
I know how to transform string literal to std::array, at compile time. But without UDL. Please see here, for inspiration.
Yes, I know C++20 will have UDL template addition, and GCC, clang have something else right now. Although I do not see how is any of that helping me.
And lastly, I know I can do this:
constexpr auto string_view_ = "String"sv ;
Unfortunately, this does not seem to be possible in C++17. A user-defined-string-literal can only match operator""X(str, len) per [lex.ext]/5. Then, the len is a function argument, and function arguments cannot be converted to template arguments. Just like you can't do this:
template <int N>
struct S {};
constexpr auto f(int n)
{
return S<n>{}; // no, n is not guaranteed to be known at compile time
}
"foo"sv works because the size isn't a template parameter of std::basic_string_view, but a "runtime" property instead which happens to benefit from constexpr. You can't do it with std::array because the size is a template parameter of std::array.
make_array works because it is not a literal operator, so it can take the size as a template parameter instead of a function parameter. Then, it can pass the template parameter to std::array. A literal operator can't do that.
In C++20, I think we can use a wrapper type like this:
template <std::size_t N>
struct helper {
std::array<char, N> string;
template <std::size_t... Is>
constexpr helper(const char (&str)[N + 1], std::index_sequence<Is...>)
:string{str[Is]...}
{
}
constexpr helper(const char (&str)[N + 1])
:helper{str, std::make_index_sequence<N>{}}
{
}
};
template <std::size_t N>
helper(const char (&str)[N]) -> helper<N - 1>;
and then use a string literal operator template:
template <helper str> // placeholder type for deduction
constexpr auto operator""_S()
{
return str.string;
}
static_assert("foo"_S == std::array{'f', 'o', 'o'});
C++20 is not finalized yet though, so I cannot speak for sure.
I am trying to implement a function which accepts a variable number of strings and forwards to a print function, which expects a char pointer and size for every string, interleaved.
Example:
std::string a = "123";
std::string b = "1234";
forward(a, b); // should call doPrint(a.c_str(), a.size(), b.c_str(), b.size())
I thought that the following should be a correct implementation, but even though it compiles the behavior is very surprising to me.
template <class ...Args>
void forward(const Args & ... args) {
doPrint( (args.c_str(), args.size())...);
}
forward(a, b) calls doPrint(3, 4), and not doPrint("123", 3, "1234", 4), as if I had written doPrint((args.size())...). The call to c_str() is ignored completely by the compiler.
I tried g++, clang, and icc with all yielding the same output. What is wrong with (args.c_str(), args.size())...?
Indeed, std::make_tuple(args.c_str(), args.size())... works as expected, but let's say I cannot change doPrint to accept and process tuples.
The comma operator is an expression whose value is the value of the last expression.
For example:
int a = (1, 2, 3, 4, 5, 6);
assert(a == 6);
What you can try instead is using tuples:
doPrint(std::tuple_cat(std::make_tuple(argc.c_str(), args.size())...));
Then doPrint will need to be changed to work with a tuple; it could unpack the tuple back into a parameter pack if desired or just work with the tuple directly.
Example unpacking tuple:
template <class Tuple, std::size_t ... indices>
doPrint(Tuple t, std::integer_sequence<size_t, indices...>)
{
doPrint(std::get<indices>(t)...);
}
template <class Tuple>
doPrint(Tuple t)
{
doPrint(t, std::make_index_sequence<std::tuple_size<Tuple>::value>());
}
There could be some problems with ambiguous function names so you may need to change the names of these helper functions, but hopefully this is enough for you to get going.
(args.c_str(), args.size()) is a comma-separated expression, meaning that only the last part (args.size()) will be passed to the function.
It will then repeat this for each parameter, so it will actually call doPrint just with the strings sizes!
You should change doPrint to use tuples instead, otherwise you have to use some crazy template meta-programming stuff.
I'd probably do it this way in order to avoid exposing tuples to the programming interface:
#include <string>
#include <utility>
#include <tuple>
extern void doPrint(...);
namespace detail {
template<std::size_t...Is, class Tuple>
void forward(std::index_sequence<Is...>, Tuple&& tuple)
{
doPrint(std::get<Is>(tuple)...);
}
}
template<class...Strings>
void forward(Strings&&... strings)
{
detail::forward(std::make_index_sequence<sizeof...(Strings) * 2>(),
std::tuple_cat(std::make_tuple(strings.data(), strings.size())...)
);
}
int main()
{
std::string a = "123";
std::string b = "1234";
forward(a, b); // should call doPrint(a.c_str(), a.size(), b.c_str(), b.size())
}
Jason Turner demonstrates a concise way to expand variadic templates using an initializer list in this video:
http://articles.emptycrate.com/2016/05/09/variadic_expansion_wrap_up.html
template< typename ... T >
void do_print(T ... args)
{
(void)std::initializer_list<int> {
(std::cout << args.c_str() << ": "
<< args.size() << "\n", 0)...
};
}
template< typename ... T >
void forward_print(T ... args)
{
do_print(args...);
}
int main(int argc, const char * argv[])
{
std::cout << "Hello, World!\n";
std::string a = "1234";
std::string b = "567";
forward_print(a, b);
return 0;
}
This works with g++ -std=c++11
I have an api that looks like this:
template<typename... Args>
Widget::Widget(std::string format_str, Args&&... args);
How would you call this method if you have a vector of strings for 'args', i.e. the args length isn't known at compile time?
What would the implementation of a wrapper function look like that would convert this to something like this?
template<typename... Args>
Widget::WrapperWidget(std::string format_str, vector<string>);
The Widget::Widget function actually does not exist, it's only a template. A function is instantiated only after you specify the number and types of the parameters. This must be done in compile time.
Since the length of the vector is only available in runtime, I can think of only this solution:
switch (v.size())
{
case 0: f(fmt); break;
case 1: f(fmt, v[0]); break;
case 2: f(fmt, v[0], v[1]); break;
case 3: f(fmt, v[0], v[1], v[2]); break;
//... etc
}
Note that those f functions are totally different functions in your executable.
Although I'm not sure that I'd like to code above in actual code.
If you wrote the interface than maybe you should extend the interface itself. If not, you may have overloads for this function.
Also reading the parameter names reveals that this is just some kind of pretty printing. You could also make a runtime counterpart to that function and pass as a single argument like this:
string s = format_vector_runtime(fmt, v);
f("%s", s);
where f is still the widget constructor like above and the first parameter is a format string meaning "take the second parameter as is" - may be different for your Widget.
The size of the vector is actually of no concern. I drew up another example of how to process a vector that's supposed to replace stuff in the format string with a variadic function-template approach. You can easily adapt that to work with variadic class-templates. The basic idea is the same:
#include <vector>
#include <string>
#include <iostream>
#include <exception>
using StrCIter = std::string::const_iterator;
void printerHelper(StrCIter& fmtPos, StrCIter fmtEnd, std::vector<std::string>& vec)
{
// if the vector is empty, we simply return without doing anything
// and instead print the next argument
auto vecIter = vec.begin ();
while(vecIter != vec.end () && fmtPos != fmtEnd)
{
if(*fmtPos == '%' && *(fmtPos + 1) == 's')
{
std::cout << *vecIter++;
fmtPos += 2;
continue;
}
std::cout << *fmtPos++;
}
}
template <typename T>
void printerHelper(StrCIter& fmtPos, StrCIter fmtEnd, const T& value)
{
std::cout << value;
fmtPos += 2;
}
void myPrintfHelper(StrCIter pos, StrCIter end)
{
// end of expansion - no more format arguments, just print remaining characters
while(pos != end)
{
if(*pos == '%')
{
throw "More format specifiers than arguments provided!";
}
std::cout << *pos++;
}
}
template <typename Head, typename ... Tail>
void myPrintfHelper(StrCIter pos, StrCIter end, Head&& head, Tail&&... tail)
{
while(pos != end)
{
if(*pos == '%' && *(pos + 1) == 's')
{
printerHelper (pos, end, head);
return myPrintfHelper(pos, end, std::forward<Tail>(tail)...);
}
std::cout << *pos++;
}
}
template <typename ... Args>
void myPrintf(const std::string& format, Args&& ... args)
{
myPrintfHelper (format.begin(), format.end (), std::forward<Args>(args)...);
}
int main()
{
std::vector<std::string> v = {"world", "magic"};
myPrintf("Hello, %s! Welcome to the %s of variadic template %s! This ... is ... C++%s!", "StackOverflow", v, 11);
return 0;
}
Basically, we want to iterate over the formate string and as soon as we hit a format specifier (in our case always represented by %s), we print stuff that is currently the head element resulting from pack expansion. If that element is a vector, we want to iterate further over the format string as long as
we don't hit the end of the format string and
we still have elements in the vector to process
Just like with printf(), superfluous arguments are ignored, if too many format specifiers are found after pack expansion has terminated, we throw an exception.
Note that the idea is inspired by Stroustrups's approach to emulate printf via variadic templates - only my version takes a std::string as format string instead of a const char*.
The good thing is that you can easily make the function more powerful simply by providing another printHelper overload that handles other types that don't provide an operator<<(std::ostream&[, ...]) or cannot be expanded to do so.
All the overload needs to do is handle adjustment of the format-string iterator and print stuff when a format specifier is hit. In my two examples, I do it with a look-ahead of 1 to determine we hit a format specifier, but there are other ways.
Incidentally, the output is:
Hello, StackOverflow! Welcome to the world of variadic template magic! This ... is ... C++11!
Following may help:
#if 1 // Not in C++11
#include <cstdint>
template <std::size_t ...> struct index_sequence {};
template <std::size_t I, std::size_t ...Is>
struct make_index_sequence : make_index_sequence < I - 1, I - 1, Is... > {};
template <std::size_t ... Is>
struct make_index_sequence<0, Is...> : index_sequence<Is...> {};
#endif // make_index_sequence
// you may use `std::tostring`
template <typename T> std::string myToString(T&& t);
class Widget
{
public:
Widget(std::string format_str, const std::vector<std::string>& v);
// this will call Widget(std::string, const std::vector<std::string>&)
template<typename... Args>
explicit Widget(std::string format_str, Args&&... args) :
Widget(format_str, std::vector<std::string>{myToString(std::forward<Args>(args))...})
{}
// This will call Widget(format_str, a[0], a[1], .., a[N - 1]) // So the Args&&... version
template <std::size_t N>
Widget(std::string format_str, const std::array<std::string, N>& a) :
Widget(format_str, a, make_index_sequence<N>())
{}
private:
template <std::size_t N, std::size_t...Is>
Widget(std::string format_str, const std::array<std::string, N>& a, const index_sequence<Is...>&) :
Widget(format_str, a[Is]...)
{}
};
I got this compile time string comparison from another thread using constexpr and C++11 (http://stackoverflow.com/questions/5721813/compile-time-assert-for-string-equality). It works with constant strings like "OK"
constexpr bool isequal(char const *one, char const *two) {
return (*one && *two) ? (*one == *two && isequal(one + 1, two + 1))
: (!*one && !*two);
}
I am trying to use it in the following context:
static_assert(isequal(boost::mpl::c_str<boost::mpl::string<'ak'>>::value, "ak"), "should not fail");
But it gives me an compilation error of static_assert expression is not an constant integral expression.
Can I do this?
The problem is that the value member of mpl::c_str is not marked as constexpr. Until the library authors decide to include support for constexpr, you are pretty much screwed, unless you are willing to modify your Boost code (or create your own version of c_str). If you decide to do so, the modification is quite simple: you just need to locate BOOST_ROOT/boost/mpl/string.hpp and replace this
template<typename Sequence>
struct c_str
{
...
static typename Sequence::value_type const value[BOOST_MPL_LIMIT_STRING_SIZE+1]
};
template<typename Sequence>
typename Sequence::value_type const c_str<Sequence>::value[BOOST_MPL_LIMIT_STRING_SIZE+1] =
{
#define M0(z, n, data) \
mpl::aux_::deref_unless<BOOST_PP_CAT(i, n), iend>::type::value,
BOOST_PP_REPEAT(BOOST_MPL_LIMIT_STRING_SIZE, M0, ~)
#undef M0
'\0'
};
by this
template<typename Sequence>
struct c_str
{
...
static constexpr typename Sequence::value_type value[BOOST_MPL_LIMIT_STRING_SIZE+1] =
{
#define M0(z, n, data) \
mpl::aux_::deref_unless<BOOST_PP_CAT(i, n), iend>::type::value,
BOOST_PP_REPEAT(BOOST_MPL_LIMIT_STRING_SIZE, M0, ~)
#undef M0
'\0'
};
};
// definition still needed
template<typename Sequence>
constexpr typename Sequence::value_type c_str<Sequence>::value[BOOST_MPL_LIMIT_STRING_SIZE+1];
Hmm, after digging a bit more, it turns out the problem is more complex than I thought. In fact, static constants can be used in constexpr; the true problem is that c_str<T>::value is an array, and your function takes pointers as parameters. As a consequence, the compiler needs to decay the array, which boils down to taking the address of its first element. Since addresses are a runtime concept, it is not possible to take the address of an object in a constexpr.
To solve the issue, I tried to write a second version of isequal that operates on arrays rather than on pointers:
template <int N, int M>
constexpr bool isequal(char const (&one)[N], char const (&two)[M], int index)
{
return (one[index] && two[index]) ?
(one[index] == two[index] && isequal(one, two, index + 1)) :
(!one[index] && !two[index]);
}
template <int N, int M>
constexpr bool isequal(char const (&one)[N], char const (&two)[M])
{
// note: we can't check whether N == M since the size of the array
// can be greater than the actual size of the null-terminated string
return isequal(one, two, 0);
}
constexpr char hello[] = "hello";
static_assert(isequal(hello, hello), "hello == hello");
constexpr char zello[] = "zello";
static_assert(!isequal(hello, zello), "hello != zello");
constexpr char hel[] = "hel";
static_assert(!isequal(hello, hel), "hello != hel");
Unfortunately, this code does not work with mpl::c_str; in fact, the problem is that static const arrays are not compile-time values, unlike integral constants. So we're back the beginning: unless value is marked as constexpr, there is no way to use it in a constant expression.
As to why the code I gave initially fails, I can't answer right now as my version of gcc (4.6) fails to compile it altogether...
After updating gcc, it turns out value needs to be defined outside the class, even though it is declared and initialized in the class (see this question). I edited the code above with the correction.