Hi!
I've been trying to make
#include <meta>
#include <string>
#include <iostream>
template<typename S>
constexpr std::string serialize(S s) {
constexpr auto ctx = std::meta::access_context::current();
std::string result = " ";
template for (constexpr auto m :
std::define_static_array(nonstatic_data_members_of(^^S, ctx))) {
result += identifier_of(m);
result += "=";
if constexpr (type_of(m) == ^^int)
result += std::string(s.[:m:] / 10, 'X');
else
result += s.[:m:];
result += " ";
};
return result;
}
struct Person {
std::string name;
int age;
};
constexpr Person john{"John", 42};
static_assert(serialize(john) == " name=John age=XXXX ");
int main() {
using namespace std;
string s = "john:";
s += serialize(john);
cout << s << endl;
}
work on Marek's reflection branch today, unfortunately it doesn't work.
I've manually reduced the problem to something which doesn't need
reflection:
#include <span>
constexpr int arr1[3] = { 1, 2, 3 };
consteval std::span <const int> foo () { return std::span <const int> (arr1); }
void
bar ()
{
#ifdef USE_EXPANSION_STMT
template for (constexpr auto m : foo ())
;
#else
static constexpr auto &&range = foo ();
static constexpr auto begin = range.begin ();
static constexpr auto end = range.end ();
static constexpr auto N = [] consteval {
std::ptrdiff_t result = 0;
for (auto i = begin ; i != end ; ++i) ++result;
return result;
}();
static_assert (N == 3);
{
static constexpr auto iter = begin + decltype(begin -
begin){std::ptrdiff_t(0)};
constexpr auto m = *iter;
}
{
static constexpr auto iter = begin + decltype(begin -
begin){std::ptrdiff_t(1)};
constexpr auto m = *iter;
}
{
static constexpr auto iter = begin + decltype(begin -
begin){std::ptrdiff_t(2)};
constexpr auto m = *iter;
}
#endif
}
where I've included also the desugarized version of the expansion stmt
(note, it is partly before https://cplusplus.github.io/CWG/issues/3044.html
because we don't still support constexpr references and so it can't
be non-static, but I guess with non-static the behavior will be the same).
The desugarized version fails to compile with both GCC trunk and clang,
the function returns a non-const value which needs to be stored into a
lifetime extended temporary and clearly both compilers create
static std::span <const int> variable for that, so obviously the rest of
the constant evaluation doesn't work.
Now, clang++ can be helped with changing
static constexpr auto &&range = foo ();
line to
static constexpr const auto &&range = foo ();
(and I wonder if https://eel.is/c++draft/stmt.expand#5.2 shouldn't be
changed accordingly to be
... constexpr const auto&& range = expansion-initializer;
). Note Jonathan suggested that perhaps in that case it should be
... constexpr const auto& range = expansion-initializer;
instead.
This doesn't help g++ though. For that one only:
static constexpr auto &&range = (const std::span <const int>) foo ();
works, set_up_extended_ref_temp uses the type of expr (i.e.
std::span <const int> here) rather than TREE_TYPE (TREE_TYPE (decl))
i.e. if range was constexpr const auto && then const std::span <const int>,
and uses that type to create the temporary as well as in
if (TREE_CONSTANT (init))
{
if (literal_type_p (type)
&& CP_TYPE_CONST_NON_VOLATILE_P (type)
&& !TYPE_HAS_MUTABLE_P (type))
and only in that case it sets
DECL_DECLARED_CONSTEXPR_P (var) = true;
DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (var) = true;
TREE_CONSTANT (var) = true;
TREE_READONLY (var) = true;
etc.
So, two questions, which compiler is right and does the expansion stmt
wording need to change?
Jakub