Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Inheriting" a class with variadic templated function

I want to write a database wrapper that can operate different types of databases (e.g. sqlite, postgres, etc.), so the code that users are writing does not change regardless of what database they actually are using.

In my head, this calls for an abstract base class like:

class database {
public:
    virtual bool query(const std::string &q) = 0;
    // Other stuff
};

class sqlite : public database {
public:
    bool query(const std::string &q) {
        // Implementation
    }
};

This looks good, but I am using variadic templates for escaping arguments inside queries (and I really like this idea, so I would want to hang on to it!), so unfortunately my base class rather looks like:

class database {
public:
    template <typename... Args>
    bool query(const std::string &q, const Args &... args) {
        // Implementation
    }
};

This stands in the way of creating an abstract class however, because templated functions cannot be virtual. The only thing I came up with so far is this construct

template <class DatabaseType>
class database {
public:
    template <typename... Args>
    bool query(const std::string &q, const Args &... args) {
        return database_.query(q, args...);
    }
private:
    DatabaseType database_;
};

While this seems to work, with all the wrappers that do nothing but call the identically named database_ functions it does not look like a very good style to me. Is this the design pattern of choice here, or is there a cleaner, or more idiomatic way?

like image 616
nijansen Avatar asked Feb 21 '23 15:02

nijansen


2 Answers

You can build a query object out of the arguments to the query function, and pass it to a virtual query_impl function.

like image 86
n. 1.8e9-where's-my-share m. Avatar answered Mar 04 '23 06:03

n. 1.8e9-where's-my-share m.


[...] is there a cleaner, or more idiomatic way

You may want to look up CRTP to achieve polymorphic behavior with templates.

template <class concrete_db> 
struct abstract_db { 
    template <typename... Args>
    void query(std::string const& q, Args const&... args) { 
        static_cast<concrete_db *>(this)->query(q, args...);
    }
};

struct postgres_db : abstract_db<postgres_db> {
    template <typename... Args>
    void query(std::string const& q, Args const&... args) { 
         // do something
    }
};
like image 30
dirkgently Avatar answered Mar 04 '23 05:03

dirkgently