geostocker
geostocker

Reputation: 1200

nesting function calls in c++ thread

I've got a threading problem that I've been trying to find an answer to on SO and Google, but with no luck.

Simply put I would want to create a thread that can execute a function that is using another function as a parameter (with its own arguments - see below).

I've followed these guides (https://www.justsoftwaresolutions.co.uk/threading/multithreading-in-c++0x-part-2-function-objects-and-arguments.html) on threading that work absolutely flawlessly, except for the above mentioned use case.

This is the specific line where I attempt to create the thread:

std::thread t(executeQuery, buildQuery(*_mVecHistIter, symbol));

And these are the nested functions definitions:

std::wstring database_con::buildQuery(vector<std::wstring> input, string symbol)
void database_con::executeQuery(wstring query)

And the error:

no instance of constructor "std::thread::thread" matches the argument list

I could simply rebuild the functions so that buildQuery starts executeQuery. But I would want to separate the calls and ensure that as much as possible is done from the class "body" for readability and maintainability down the line.

Any kind of advice, pointers or ideas of where I can find more material on the subject would be appreciated! :)

I'm using MSVC 2015.

Edit:

#include "stdafx.h"
#include "database_con.h"

////////////////////////////////////////////////////////////////////////
// Show errors from the SQLHANDLE

void database_con::show_error(unsigned int handletype, const SQLHANDLE& handle)
{
    SQLWCHAR sqlstate[1024];
    SQLWCHAR message[1024];
    if (SQL_SUCCESS == SQLGetDiagRec(handletype, handle, 1, sqlstate, NULL, message, 1024, NULL))
        wcout << "Message: " << message << "\nSQLSTATE: " << sqlstate << endl;
}

std::wstring database_con::StringToWString(const std::string& s)
{
    std::wstring temp(s.length(), L' ');
    std::copy(s.begin(), s.end(), temp.begin());
    return temp;
}

////////////////////////////////////////////////////////////////////////
// Builds the stored procedure query.

std::wstring database_con::buildQuery(vector<std::wstring> input, string symbol)
{
    std::wstringstream builder;
    builder << L"EXEC sp_addHistorical " << "@Symbol='" << L"" << StringToWString(symbol) << "'," <<
        "@Date='" << (wstring)L"" << input.at(0) << "'," <<
        "@Open=" << (wstring)L"" << input.at(1) << "," <<
        "@Close=" << (wstring)L"" << input.at(2) << "," <<
        "@MaxPrice=" << (wstring)L"" << input.at(3) << "," <<
        "@MinPrice=" << (wstring)L"" << input.at(4) << "," <<
        "@Volume=" << (wstring)L"" << input.at(5) << ";";
    return builder.str();
}

void database_con::executeQuery(wstring query) {

    if (SQL_SUCCESS != SQLExecDirectW(stmt, const_cast<SQLWCHAR*>(query.c_str()), SQL_NTS)) {
        std::cout << "Execute error " << std::endl;
        show_error(SQL_HANDLE_STMT, stmt);
        std::wcout << L"Unsuccessful Query: " << query << std::endl;
    }
    // Close Cursor before next iteration starts:
    SQLRETURN closeCursRet = SQLFreeStmt(stmt, SQL_CLOSE);
    if (!SQL_SUCCEEDED(closeCursRet))
    {
        show_error(SQL_HANDLE_STMT, stmt);
        // maybe add some handling for the case that closing failed.
    }
}
////////////////////////////////////////////////////////////////////////
// Constructs a database connector object with the historical data and its symbol

database_con::database_con(std::vector<std::vector<std::wstring>> historical, string symbol){
    /*
    Set up the handlers
    */

    /* Allocate an environment handle */
    SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
    /* We want ODBC 3 support */
    SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3, 0);
    /* Allocate a connection handle */
    SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);

    /* Connect to the DSN */
    SQLDriverConnectW(dbc, NULL, L"DRIVER={SQL Server};SERVER=ERA-PC-STUART\\JBK_DB;DATABASE=master;UID=geo;PWD=kalle123;", SQL_NTS, NULL, 0, NULL, SQL_DRIVER_COMPLETE);
    /* Check for success */
    if (SQL_SUCCESS != SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt))
    {
        show_error(SQL_HANDLE_DBC, dbc);
        std::cout << "Failed to connect";
    }
    std::cout << "Building and executing the query" << std::endl;

    for (_mVecHistIter = historical.begin();
        _mVecHistIter != historical.end();
        _mVecHistIter+5) {
        std::thread t(&database_con::executeQuery, *this, buildQuery(*_mVecHistIter, symbol));
        std::thread t2(&database_con::executeQuery, *this, buildQuery(*_mVecHistIter, symbol));
        std::thread t3(&database_con::executeQuery, *this, buildQuery(*_mVecHistIter, symbol));
        std::thread t4(&database_con::executeQuery, *this, buildQuery(*_mVecHistIter, symbol));
        std::thread t5(&database_con::executeQuery, *this, buildQuery(*_mVecHistIter, symbol));
        t.join();
        t2.join();
        t3.join();
        t4.join();
        t5.join();

        //executeQuery(buildQuery(*_mVecHistIter, symbol)); 

    }

    /*_mSymbol = symbol;
    std::wstringstream stream(StringToWString(historical));
    std::wstring line;
    int row = 0;
    while (std::getline(stream, line)) {
        if (row > 0) {
            vector<wstring> vHistorical = parseData(L"" + line, ',');
            std::wstring SQL = buildQuery(vHistorical, _mSymbol);
            if (SQL_SUCCESS != SQLExecDirectW(stmt, const_cast<SQLWCHAR*>(SQL.c_str()), SQL_NTS)) {
                std::cout << "Execute error " << std::endl;
                show_error(SQL_HANDLE_STMT, stmt);
                std::wcout << L"Unsuccessful Query: " << SQL << std::endl;
            }
            // Close Cursor before next iteration starts:
            SQLRETURN closeCursRet = SQLFreeStmt(stmt, SQL_CLOSE);
            if (!SQL_SUCCEEDED(closeCursRet))
            {
                show_error(SQL_HANDLE_STMT, stmt);
                // maybe add some handling for the case that closing failed.
            }
        }
        row++;
    }*/
    std::cout << "Query " << _mSymbol << " ready" << std::endl;

}

database_con::~database_con() {
    std::cout << "The database object has been deleted" << std::endl;
}

Upvotes: 0

Views: 625

Answers (1)

Zereges
Zereges

Reputation: 5209

OK, I probably misunderstood the question. You don't want create a thread using a function, having one of its arguments another function. But rather create a thread using a function and having its parameter computed by another function. There is nothing particular about that. You probably just forget to add & operator to take an address of the function.

std::thread t(&executeQuery, buildQuery(*_mVecHistIter, symbol));
              ^-- note the &

EDIT
So, you forget to mention that database_con is a class. In order to use class member function as param for function acception pointer to function as parameter (in your case std::thread::thread) you have to use fully qualified name with operand & (&database_con::executeQuery). However, since that is non static member function, it will require its first argument pointer to instance of such object (google std::thread non-static member function).

std::thread t(&database_con::executeQuery, this, buildQuery(*_mVecHistIter, symbol));

Previous answer

Did you considered using std::function?

#include <functional>
#include <thread>

int foo(int arg1, int arg2)
{
    return arg1 + arg2;
}
void bar(std::function<int(int, int)> fnc, int arg1, int arg2)
{
    fnc(arg1, arg2);
}
int main()
{
    std::thread thr{&bar, &foo, 1, 2};
}

Upvotes: 2

Related Questions