rapidyaml: Serialize std::filesystem::path error

Hi how to correctly supply to_chars overload for std::filesystem::path? All my other custom types work by overloading write or to_chars but with filesystem::path I always get a compile error which says that it somehow does not find a matching function. What am I doing wrong?

#include <filesystem>

#define RYML_SINGLE_HDR_DEFINE_NOW
#include "rapidyaml.hpp"

size_t to_chars(ryml::substr buf, std::filesystem::path const& p) { 
	return to_chars(buf, p.generic_string());
}

int main(int argc, char** argv)
{
	std::filesystem::path path = "C:/sample";
	ryml::Tree tree{};
	ryml::NodeRef root = tree.rootref();
	root << path;
	return 0;
}
./rapidyaml.hpp:19835:22: error: no matching function for call to 'to_chars'
 19835 |         size_t num = to_chars(rem, a);
       |                      ^~~~~~~~
./rapidyaml.hpp:21107:29: note: in instantiation of function template specialization 'c4::yml::Tree::to_arena<std::filesystem::path>' requested here
 21107 |         csubstr s = m_tree->to_arena(v);
       |                             ^
./rapidyaml.hpp:21561:8: note: in instantiation of function template specialization 'c4::yml::NodeRef::set_val_serialized<std::filesystem::path>' requested here
 21561 |     n->set_val_serialized(v);
       |        ^
./rapidyaml.hpp:21233:9: note: in instantiation of function template specialization 'c4::yml::write<std::filesystem::path>' requested here
 21233 |         write(this, v);
       |         ^
main.cpp:22:7: note: in instantiation of function template specialization 'c4::yml::NodeRef::operator<<<std::filesystem::path>' requested here
   22 |         root << path;

About this issue

  • Original URL
  • State: closed
  • Created 3 months ago
  • Comments: 16 (8 by maintainers)

Most upvoted comments

Finally found what’s wrong: ADL (AKA Koenig lookup) fails because of a missing namespace.

You need to declare the to_chars() overload in one of the namespaces: either std::filesystem or c4 or c4::yml.

So this will work:

#define RYML_SINGLE_HDR_DEFINE_NOW
#include "./rapidyaml-0.5.0.hpp"  // freshly downloaded from the release

struct vec2 { int x, y; };
size_t to_chars(ryml::substr buf, vec2 v) { return ryml::format(buf, "({},{})", v.x, v.y); }

#include <filesystem> // can be included also before the ryml header

namespace std::filesystem {
size_t to_chars(ryml::substr buf, std::filesystem::path const& p) { return to_chars(buf, p.generic_string()); }
}

#include <iostream>
int main(int argc, char** argv)
{
	ryml::Tree tree{};
	ryml::NodeRef root = tree.rootref();
	root |= ryml::SEQ;
	root[0] << std::filesystem::path{"C:/sample"};
	root[1] << vec2{2,3};
	std::cout << root;
	return 0;
}

and this as well:

// ...

namespace c4 {
size_t to_chars(ryml::substr buf, std::filesystem::path const& p) { return to_chars(buf, p.generic_string()); }
}

// ...

… and this as well:

// ...

namespace c4::yml {
size_t to_chars(ryml::substr buf, std::filesystem::path const& p) { return to_chars(buf, p.generic_string()); }
}

// ...

I’ve had to face this problem several times before. This time I forgot all about it, apparently… Also, if you look for example at the to_chars() implementation for std::string, you’ll notice that to_chars() is under c4. That is the hint on how to do it.

So you can use the clean version of the header, provided you define to_chars() in one of the three namespaces above.

Note that unfortunately you cannot use ryml, because ryml is an importing namespace for c4::yml:

// ...

namespace ryml { // error, sorry! ryml is merely an importing namespace for c4::yml
size_t to_chars(ryml::substr buf, std::filesystem::path const& p) { return to_chars(buf, p.generic_string()); }
}

// ...

As for adding std::filesystem::path to the library’s default std helpers, that’s unfortunately not possible because of the generic vs native question. If it were added, one of the two would be have to be forced on the users, and that’s not a legitimate thing for a library like this.

Also, let me point out that if the reason you’re using std::filesystem::path is merely for path manipulation, it may be that you can just use the csubstr class, which provides Path-like manipulation methods (search for Path-like in that link), and spares you the inherent allocation in p.generic_string().

Yes I think it makes sense to add fs::path with other provided overloads the only question would be which format to choose. Keep backslashes on windows or convert to forward slashes with generic_string().

Anyway, I’ve added a few lines inside the header and it works for me as a workaround for now. The library overall is really pleasant to use btw!