I need to develop a manager that takes a struct and stores its members to a database row, and that fills a struct with values from a database row again. Objects of the same struct are stored in the same table. This is our example strcut.
struct Person
{
    std::string Name;
    int Age;
    // more members that shouldn't be stored or loaded
    // ...
};
My goal is to load and store structs into their table with one line.
// example for storing
unsigned int id = 42;          // not hardcoded of course
Person paul = { "Paul", 8 };   // instantiate the struct
Data->Save<Person>(id, *paul); // table name is derived from template argument
// example for fetching
unsigned int id = 42;
Person paul;
Data->Load<Person>(id, *paul);
All structs could be forced to implement serialization and deserialization functions through inheritance, or I could make use of macro based reflection as suggested by Matthieu M. in the answers. But I cannot make changes to the structs that get stored. So I decided that those structs must be registered first, providing their serialization and deserialization functions. (By which I mean conversion from an instance of the struct to a database row object and vice versa.)
class Data
{
    typedef std::function<row(T*) Serialization;
    typedef std::function<void(row, T*) Deserialization;
    typedef std::pair<Serialization, Deserialization> Registereds;
    std::unordered_map<std::type_index, Registered> List;
public:
    template <typename T>
    Register<T>(std::function<row(T*) Serialize,
                std::function<void(row, T*) Deserialize)
    {
        // create a table based on the template argument
        std::string name = typeid(T).name();
        // ...
        // add conversion functions to the list
        auto index = std::type_index(typeid(T));
        List.insert(index, std::make_pair(Serialize, Deserialize));
    }
    // load and save functions shown above go here
    // ...
};
You may have already noticed the row data type I used in this example. That exactly is my problem! For every struct the row contains different fields of different types. So what data structure can I use for holding a row?
I though about using some kind of std::tuple. For example if a tuple could have string keys, that would be a possibility. Then, I would have to completely templalize everything to work with different structs. It could look similar to this draft below. But I have to idea how to store the conversion functions of different structs in the same List then, without using void* pointers.
Data->Register<Person, std::string, int>(
    [](Person *Instance) -> std::tuple<std::string, int>{ /* ... */ }, 
    [](std::tuple<std::string, int> Row, Person *Instance) -> void{ /* ... */ }
);
How can I represent the database row object? If my template approach is a good way, how can I templatize everything?
Update in response to Matthieu M.'s answer
You suggested using type traits. This is what I tried. But I still don't know how to define the type for representing a row.
Traits
template <class T>
struct data_traits{ };
struct data_traits<Person>
{
    struct row{ int Age; std::string Name; }; // right to do here?
    row serialize(Person *Instance)
    {
        // ...
    }
    void deserialize(row Data, Person *Instance)
    {
        // ...
    }
};
Data manager
class Data
{
    template <typename T>
    void Load(uint64_t Id, T *Instance)
    {
        auto traits = data_traits<T>;
        std::string query = "SELECT * FROM "
                          + typeid(T).name()
                          + " WHERE id = "
                          + std::to_string(Id);
        // fetch result from database
        // ...
        traits::row result;
        // convert SQL result into type dependent row type
        // but how to do that?
        // ...
        traits.deserialize(result, Instance);
    }
};
 
     
    