Search code examples
c++templatesstd

How to invert templates dependency?


In case I want to make a generic class template ResourceManager which serializes its content calling serialize function (or template), how to make call to serialize below compilable?

I don't want to declare a function template for vectors, because it is assumed that the project will use ResourceManager with different types of containers and I don't want to declare all possible containers or include this file in the middle of my sources after such declaration.

Can I somehow avoid this declaration or inverse the dependencies so that would work with std containers as well as with my types?

In case I worked only with my types, I would make serialize their class member, but I want to work with std containers, as well without wrapping them.

#include <fstream>
#include <iostream>
#include <vector>

// With this forward declaration it works for `std::vector`
// template <class T>
// void serialize(std::ofstream& ofs, std::vector<T>& v);

template <class Resource>
struct ResourceManager {
    Resource& resourse;

    ResourceManager(Resource& resourse) : resourse(resourse) {}

    void store() {
        std::ofstream ofs("vector_bin.vec", std::ios::trunc | std::ios::binary);

        serialize(ofs, resourse);
    }
};

template <class T>
void serialize(std::ofstream& ofs, std::vector<T>& v) {
    auto size = v.size();
    ofs.write((const char*)&size, sizeof(size));

    ofs.write((const char*)v.data(), v.size() * sizeof(int));
}

int main()
{
    std::vector<int> v = { 1,2,3 };

    ResourceManager rm(v);

    rm.store();
}

Here is the live demo.


Solution

  • You can replace serialize() function with a trait class, and then partially specialize the trait for each container:

    template <class Resource>
    struct Serializer
    {
        static void write(std::ofstream& ofs, Resource& v) = delete;
    };
    
    template <class Resource>
    struct ResourceManager {
        Resource& resourse;
    
        ResourceManager(Resource& resourse) : resourse(resourse) {}
    
        void store() {
            std::ofstream ofs("vector_bin.vec", std::ios::trunc | std::ios::binary);
    
            Serializer<Resource>::write(ofs, resourse);
        }
    };
    
    template <class T>
    struct Serializer<std::vector<T>>
    {
        static void write(std::ofstream& ofs, std::vector<T>& v) {
            auto size = v.size();
            ofs.write((const char*)&size, sizeof(size));
    
            ofs.write((const char*)v.data(), v.size() * sizeof(int));
        }
    };