-1

I'm building a minimal binary parser/serializer for one of my projects and I got this error that seems inconsistent at first. I have two top functions I want to expose T parse<T>(bytes) and bytevec serialize<T>(T const&) and from these two templated functions I delegate the serialization to overloaded functions based on the specific type provided. The problem is that the parsing side works fine, but the serialization side doesn't compile because it cannot see the overloaded function if it is defined after the top serialize function.

using bytevec = std::vector<uint8_t>;
using bytes = std::span<const uint8_t>;

template <typename T>
bytevec serialize(T const& data) {
    bytevec b{};
    serialize(data, b); // error: no matching function for call to 'serialize(const int&, bytevec&)
    return b;
}

void serialize(int data, bytevec& b) {
    b.push_back(0xff);
    b.push_back(0xaa);
}

template <typename T>
struct tag {};

template <typename T>
T parse(bytes b) {
    return parse(b, tag<T>{}); // fine
}

int parse(bytes b, tag<int>) { return b[1]; }

void test() {
    static std::array<uint8_t, 2> data{0xff, 0xaa};
    auto res1 = parse<int>(std::span(data));
    auto res2 = serialize(res1);
}

Link to compiler explorer

Why is it ok for the top parse function to call an overloaded function defined later (int parse(bytes, tag<int>) and for the serialize function the overloaded function needs to be defined before in order to compile?

Thank you

Jarod42
  • 203,559
  • 14
  • 181
  • 302
petoknm
  • 11
  • 6
  • 1
    Questions should really be self-contained. Please post the code _here_ as a [mre], with enough information so that no one has to follow a link. – ChrisMM Mar 16 '22 at 15:47
  • It sounds like you’re asking about pre declarations. C++ is not a multi pass compiler type of language. All functions must be declared before usage. – Taekahn Mar 16 '22 at 15:58

1 Answers1

0

ADL doesn't work for built-in types such as int.

  • For

    template <typename T>
    bytevec serialize(T const& data) {
        std::vector<uint8_t> b{};
        serialize(data, b);
        return b;
    }
    

    With T=int

    serialize(data, b); uses previous declaration, and can be looked-up in same namespace than std::vector<uint8_t> (so std). But no previous declarations and no std::serialize.

  • For

    template <typename T>
    T parse(bytes b) {
        return parse(b, tag<T>{});
    }
    

    with T=int

    parse(b, tag<int>{}) uses previous declaration, and can be looked-up in same namespace than tag<int> (so the global one) and std::span<const uint8_t> (so std). No previous declaration nor std::parse, but ::parse(int, tag<int>{}) exist.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • I tried using fully qualified function name `::serialize` but that also doesn't work. – petoknm Mar 17 '22 at 09:21
  • Ok, I found the answer, unqualified name lookup inside function templates uses the template definition context by default and only for ADL it uses both template definition and template instantiation context. – petoknm Mar 17 '22 at 09:42