1

I have managed to make a library to help me making request to SqlLite library, specially those related with BLOB files.

The problem came because I feel that the implementation can be improved, but I do not have enought knoledge to achieve it. Any help with this couple of issues:

  1. Some functions can be called using a SQL statement as a c-pure string or a std::string. Both of them finally calls the same function final implementation. The problem cames by an unambiguos call to template function if I try to remove the '_' for function declaration and calling (please see source code). I understand this is a minor isssue, but I wonder if is there any way to achieve it.

  2. I have try to use the same code for requests using files to load a BLOB in DB, and for requests not using BLOBS. The diference is that first ones uses files streams as parameters, and second ones do not have any stream parameter. SqlLite force to call an specific function to executute the sql statement, so I need to use a "if consexpr..." conditional clause to separate the case with 0 arguments from the case the 1 or more stream arguments.

Source code is presented ahead:

#include <string>
#include <sstream>
#include <utility>

class sqlite3 {};
class sqlite3_stmt {};
void sqlite3_step(sqlite3_stmt*) {}
sqlite3* db;

//Auxiliary BLOB functions
template<typename T>
bool sqlite3_blob(sqlite3_stmt*, const T& data, int col);

template<>
bool sqlite3_blob<std::ostringstream>(sqlite3_stmt* statement, const std::ostringstream& data, int col)
{
    //std::string buffer{ data.str() };
    return true;
}

//sqlite3 automatization request
template<typename... Args, std::size_t... Is>
constexpr bool sqlite3_request_(sqlite3* db, const char* sql, Args&&... args, std::index_sequence<Is...>)
{
    sqlite3_stmt* statement{};
    (sqlite3_blob(statement, std::forward<Args>(args), Is + 1), ...);

    if constexpr (sizeof...(Args) == 0) sqlite3_step(statement);//Horrible conditional statement. Is there any way of getting rid of it???

    return true;
}

template<typename... Args>
constexpr bool sqlite3_request(sqlite3* db, const char* sql, Args&&... args)
{
    return sqlite3_request_<Args...>(db, sql, std::forward<Args>(args)..., std::make_index_sequence<sizeof...(Args)>{});//Need to change the name because calling function is not able to unambiguosly called the right on
}

//For std::string arguments:
 template<typename... Args>
constexpr bool sqlite3_request(sqlite3* db, const std::string& sql, Args&&... args)//args contain loading data for blobs, if any
{
    return sqlite3_request_<Args...>(db, sql.c_str(), std::forward<Args>(args)..., std::make_index_sequence<sizeof...(Args)>{});// Possible to improve? Unambiguos calling to function if '_' eliminated in function calling and declaration
}

int main()
{
    const std::ostringstream data;
    std::string sql = "INSERT INTO scenarios(file) VALUES(?);";//file is a BLOB field
    sqlite3_request(db, sql, data);
}

Please note the I have clean and mock all SqlLite functions. This is not a question about SqlLite but about improving templates style.

Also, here it is a coliru link http://coliru.stacked-crooked.com/a/ec8e8bf65f451b20 to stated source code.

cigien
  • 57,834
  • 11
  • 73
  • 112
Pablo
  • 557
  • 3
  • 16
  • 1
    _Some functions can be called using a SQL statement as a c-pure string or a std::string_ You don't need to provide separate overloads for these. `const std::string &` will bind to both. – Paul Sanders Jun 21 '21 at 23:56
  • @Paul Sanders: Yes, you are right, but I have done like that as a matter of efficiency. Some of the queries are fixed and compile time defined, so using a std::string always forces them to use dynamic memory and heap. That is my guess, maybe I can be wrong and modern compilers are intelligent enought to optimeze those strings by themselves using std::string. – Pablo Jun 22 '21 at 09:02

1 Answers1

1

There are some ways to help. For the first question you can use namespaces to get ambiguous calls fixed. Regarding the second question , in case that you don't want to use if constexpr, you can also define nullptr_t as a parameter type then call it with nullptr. This may not fit most of your expectation but hope you would be happy with that

#include <string>
#include <sstream>
#include <utility>

class sqlite3 {};
class sqlite3_stmt {};
sqlite3* db;

void sqlite3_blob(sqlite3_stmt*,std::nullptr_t, int) {}

//Auxiliary BLOB functions
template<typename T>
bool sqlite3_blob(sqlite3_stmt*, const T& data, int col);

template<>
bool sqlite3_blob<std::ostringstream>(sqlite3_stmt* statement, const std::ostringstream& data, int col)
{
    //std::string buffer{ data.str() };
    return true;
}
namespace details {
//sqlite3 automatization request
template<typename... Args, std::size_t... Is>
constexpr bool sqlite3_request(sqlite3* db, const char* sql, Args&&... args, std::index_sequence<Is...>)
{
    sqlite3_stmt* statement{};
    (sqlite3_blob(statement, std::forward<Args>(args), Is + 1), ...);

    return true;
}
}

template<typename... Args>
constexpr bool sqlite3_request(sqlite3* db, const char* sql, Args&&... args)
{
    return details::sqlite3_request<Args...>(db, sql, std::forward<Args>(args)..., std::make_index_sequence<sizeof...(Args)>{});//Need to change the name because calling function is not able to unambiguosly called the right on
}

//For std::string arguments:
 template<typename... Args>
constexpr bool sqlite3_request(sqlite3* db, const std::string& sql, Args&&... args)//args contain loading data for blobs, if any
{
    return details::sqlite3_request<Args...>(db, sql.c_str(), std::forward<Args>(args)..., std::make_index_sequence<sizeof...(Args)>{});// Possible to improve? Unambiguos calling to function if '_' eliminated in function calling and declaration
}

int main()
{
    const std::ostringstream data;
    std::string sql = "INSERT INTO scenarios(file) VALUES(?);";//file is a BLOB field
    sqlite3_request(db, sql, data);
    sqlite3_request(db, sql, nullptr);
}
nhatnq
  • 1,173
  • 7
  • 16