Hi, first post. I've recently been introduced to templates and am hoping to implement them into a project I am working on; however, I've run into a few problems with design. The program I am writing uses an `Sqlite3` database, for which I am creating a wrapper to complete and store queries in an `std::vector`. I have a number of different classes to handle each table in the database, and created the function below to handle any class type.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
template<class T>
void query(sqlite3* db, const char* query, vector<T>& vec) {
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(db, query, -1, &statement, 0) == SQLITE_OK) {
int cols = sqlite3_column_count(statement);
vector<int> index;
for (int col = 0; col < cols; ++col) {
index.push_back(col);
}
int result = 0;
while (true) {
result = sqlite3_step(statement);
if (result == SQLITE_ROW) {
vec.push_back(T(
sqlite3_column_int(statement, index[0]),
(char*)sqlite3_column_text(statement, index[1]),
(int)sqlite3_column_int(statement, index[2]),
(int)sqlite3_column_int(statement, index[3]),
(char*)sqlite3_column_text(statement, index[4]),
(int)sqlite3_column_int(statement, index[5])));
}
else {
break;
}
}
sqlite3_finalize(statement);
}
string error = sqlite3_errmsg(db);
if (error != "not an error") cout << query << " " << error << endl;
sqlite3_close(db);
}
|
However, while the above works perfectly well I am having difficulty choosing a manner in which to apply it (or something similar) to all classes. As I said, each class maps to a particular table in the database, and its members to the values in the columns. In order to decrease the size of the database I have elected to store categorical information as `int`s and more specific (non-recurring) information as `string`s. For example the above is the implementation that corresponds to the following class `Car`.
The header:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
class Car {
private:
int _carID;
string _carModelName, _carEngineManufacturer;
int _carModelYear, _carColor, _carEngineDisplacement;
public:
Car(int carID, string carModelName, int carModelYear, int carColor, string carEngineManufacturer, int carEngineDisplacement) {
_carID = carID;
_carModelName = carModelName;
_carModelYear = carModelYear;
_carColor = carColor;
_carEngineManufacturer = carEngineManufacturer;
_carEngineDisplacement = carEngineDisplacement;
}
int displayCarID() const;
string displayCarModelName() const;
string displayCarModelYear() const;
string displayCarColor() const; // if _carColor == 1 return "RED"; _carColor == 2 return "BLUE"; ect...
string displayCarEngineManufacturer() const;
string displayEngineDisplacement() const;
};
|
The `std::vector` containing the data for each class is then used to display it (again specific to the above) as such:
1 2 3 4 5 6 7 8 9 10 11 12
|
template<class T>
void displayList(const vector<T> &ex) {
for (vector<T>::const_iterator it = ex.begin(); it != ex.end(); ++it) {
cout << it->displayCarID() << endl;
cout << it->displayCarModelName() << endl; // model name
cout << it->displayCarModelYear() << endl; // year
cout << it->displayCarColor() << endl; // color
cout << it->displayCarEngineManufacturer() << endl; // engine manufacture
cout << it->displayEngineDisplacement() << endl; // engine displacement
cout << "\n" << endl;
}
}
|
On to the question.
Is it possible to use a template for the above `query` function in order to avoid replicating it for each individual class? If not, what is the preferred design (or your own personal advice) for simplifying a problem like this?
Restrictions:
1) The `sqlite3` C++ interface returns queries in `C` style datatypes. For example in the first code snippet in order to get the string `carEngineManufacturer` required for the constructor of `Car` a C style cast is required as `(char*)sqlite3_column_text(statement, index[1])`.
2) Since the classes vary, another constructor may require a different order or number or arguments. IE creating a vector of planes. `Plane(int planeName, string planeModelName, int planeModelYear, string countryOfOrigin, int planeType);`
Considerations:
I considered creating a `base` class to derive from as all classes will share at least an `ID` and `Name` member, however it seems as though that would not be altogether very beneficial. Moreover, as none of the classes have, or are aware of a `vector` class member adding a specific function to their implementations would give a `non-static` or `storage` error.
I also thought about specializing the template for each data class type, which at the moment is the best solution I am aware of. Albeit, that would require the majority of the same code to be copied more than 10 times changing only that is within the `if (result == SQLITE_ROW) { }` brackets, and was curious if there is a better/simpler solution I am overlooking. I would greatly appreciate any other insight or advice, thanks in advance.
*Note – If it helps, the `int cols = sqlite3_column_count(statement);` line the template function returns the number of columns for the specific query which could possibly be used to determine the class.