Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > C/C++ программирование > Общие вопросы C/C++
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 16.06.2017, 00:12   #1
Aoizora
Заблокирован
 
Регистрация: 11.11.2016
Сообщений: 261
По умолчанию Передать контейнер в callback при выполнении запроса sqlite

Мне нужно получить порцию данных из базы SQLite. Для этого я вызываю функцию:

Код:
const auto ret = sqlite3_exec(db, _query.c_str(), callback, (void *)data, &err);
Ее параметры - функция callback, в которую передаются извлеченные данные, и data - аргумент, который программист может передать в callback. Это может быть строка, тогда при вызове функции callback внутри нее можно распечатать эту строку. Как я понимаю, этот параметр нужен для возврата данных из колбэка.

И у меня есть недописанный класс, в котором предполагается хранить результаты запроса:

Код:
class query_result
{
public:
	query_result(int _count,
                 const char ** _argv,
                 const char ** _columns);
	std::string operator[](const std::string _key);
private:
	std::map<std::string, std::string> result;
};
Вернуть объект этого класса из колбэка нельзя, поэтому надо что-то передать в колбэк и заполнить это "что-то" так, чтобы изменения сохранились снаружи. Как это реализовать?

Сейчас я думаю про классический сишный вариант - обернуть query_result в структуру и передать ее адрес. Пытался использовать shared_ptr, но он не кастуется в void *. Что делать?
Aoizora вне форума Ответить с цитированием
Старый 19.06.2017, 12:50   #2
alexzk
Форумчанин
 
Регистрация: 12.04.2017
Сообщений: 889
По умолчанию

В аргумент data обычно предполагается передать указатель на какую-то вашу структуру, которую заполнит функция. Можно рассматривать саму ф. калбек, как статик мембер, которому дают указатель на объект (дата).

...сам эту фигню не люблю, в Lua есть подобное, я изменил дефайн калбека на std::function и перекомпилировал - теперь свободно передаются любые ф. и лямбды в луа у меня

...еще в эти сишные ф. можно передать мемберы класса, но с ограничением - прийдется делать через шаблоны и нумеровать их вручную уникально.

Вот пример рабочий - лямбда идет в сишный калбек (каждая "новая" такая лямбда, т.е. вызов С ф./ lambdacb::to_callback , должна иметь уникальный ID для всего проекта):

Код:
 template <class Src>
        int load(const Src& src, const std::string& chunkName = "defchunk", const std::string& mode = "bt") const
        {
            //FIXME: that may work for 1 plugin only - please re-think it because it is STATIC in fact
            bool done = false;
            auto reader = [&src, &done](lua_State *L,
                          void *data,
                          size_t *size) -> const char *
            {
                (void)L;
                (void)data;
                const char* r = (done)?nullptr:const_cast<Src*>(&src)->data();
                *size = src.size();
                done = true;
                return r;
            };

            return lua_load(state.get(), (lua_Reader)lambdacb::to_callback<LUA_UNIQUE_CB_BEGIN + 1>(reader), nullptr, chunkName.c_str(), mode.c_str());
        }
А вот тут жутко черная магия:

Код:
//all black magic below allows to use lambdas as C-functions pointers ... in trade of - it must set unique ID at compile time amoung whole project
namespace lambdacb {

    template <typename Function>
    struct function_traits
            : public function_traits<decltype(&Function::operator())>
    {};

    template <typename ClassType, typename ReturnType, typename... Args>
    struct function_traits<ReturnType(ClassType::*)(Args...) const>
    {
        typedef ReturnType (*pointer)(Args...);
        typedef std::function<ReturnType(Args...)> function;
    };

    template <typename Function>
    typename function_traits<Function>::pointer
    to_function_pointer (const Function& lambda)
    {
        return static_cast<typename function_traits<Function>::pointer>(lambda);
    }

    template <typename Function>
    typename function_traits<Function>::function
    to_function (const Function& lambda)
    {
        return static_cast<typename function_traits<Function>::function>(lambda);
    }
    typedef void(*funcptr_type)();


    //some more black magic

    template <const size_t _UniqueId, typename _Res, typename... _ArgTypes>
    struct fun_ptr_helper
    {
    public:
        typedef std::function<_Res(_ArgTypes...)> function_type;

        static void bind(function_type&& f)
        { instance().fn_.swap(f); }

        static void bind(const function_type& f)
        { instance().fn_=f; }

        static _Res invoke(_ArgTypes... args)
        { return instance().fn_(args...); }

        typedef decltype(&fun_ptr_helper::invoke) pointer_type;
        static pointer_type ptr()
        { return &invoke; }

    private:
        static fun_ptr_helper& instance()
        {
            static fun_ptr_helper inst_;
            return inst_;
        }

        fun_ptr_helper() {}

        function_type fn_;
    };


    //_UniqueId must be manually set at compile time to unique value, that generates unique static stub

    template <const size_t _UniqueId, typename _Res, typename... _ArgTypes>
    typename fun_ptr_helper<_UniqueId, _Res, _ArgTypes...>::pointer_type
    get_fn_ptr(const std::function<_Res(_ArgTypes...)>& f)
    {
        fun_ptr_helper<_UniqueId, _Res, _ArgTypes...>::bind(f);
        return fun_ptr_helper<_UniqueId, _Res, _ArgTypes...>::ptr();
    }


    template <const size_t _UniqueId, typename Function>
    funcptr_type
    to_callback (const Function& lambda)
    {
        return (funcptr_type)get_fn_ptr<_UniqueId>(to_function(lambda));
    }
}

Последний раз редактировалось alexzk; 19.06.2017 в 13:06.
alexzk вне форума Ответить с цитированием
Старый 19.06.2017, 13:18   #3
waleri
Старожил
 
Регистрация: 13.07.2012
Сообщений: 6,493
По умолчанию

Цитата:
Сообщение от Aoizora Посмотреть сообщение
обернуть query_result в структуру и передать ее адрес. Пытался использовать shared_ptr, но он не кастуется в void *. Что делать?
В query_result добавить shared_ptr и передавать указатель на query_result, которая должна создаваться и удаляться автоматически, скрыто от пользователя.
waleri вне форума Ответить с цитированием
Старый 19.06.2017, 13:21   #4
alexzk
Форумчанин
 
Регистрация: 12.04.2017
Сообщений: 889
По умолчанию

Цитата:
Сообщение от waleri Посмотреть сообщение
В query_result добавить shared_ptr и передавать указатель на query_result, которая должна создаваться и удаляться автоматически, скрыто от пользователя.
Ну он не совсем то хочет ....хочет в С++ стиле, чтоб память не текла, и лишние в privat сидит ... ну вобщем я так тоже люблю - выше вон, все в лямбде
Т.е. передавать в ф. указатель просто так нельзя - сразу встает проблема времени жизни в полный рост и отслеживания, например, не удалил ли shared_ptr память, которая для калбека в другом потоке.

Хотя, если вызов гарантировано блокирующий (т.е. нет параллельности), то можно и так:

Код:
shared_ptr<Result> ptr (new Result());
const auto ret = sqlite3_exec(db, _query.c_str(), callback, ptr.get(), &err);

Последний раз редактировалось alexzk; 19.06.2017 в 13:28.
alexzk вне форума Ответить с цитированием
Старый 19.06.2017, 13:40   #5
waleri
Старожил
 
Регистрация: 13.07.2012
Сообщений: 6,493
По умолчанию

Цитата:
Сообщение от Aoizora Посмотреть сообщение
Пытался использовать shared_ptr, но он не кастуется в void *. Что делать?
Только сейчас обратил внимание.
Все прекрасно кастуется: static_cast<void*>(ptr.get());
waleri вне форума Ответить с цитированием
Старый 19.06.2017, 16:43   #6
Aoizora
Заблокирован
 
Регистрация: 11.11.2016
Сообщений: 261
По умолчанию

>А вот тут жутко черная магия

По-моему, это просто портянка копрокода. Из-за такого когда и говорят о том, что хаскель лучше крестов. А что насчет производительности такого кода по сравнению с аналогом на ФП-языках?
Aoizora вне форума Ответить с цитированием
Старый 19.06.2017, 16:54   #7
alexzk
Форумчанин
 
Регистрация: 12.04.2017
Сообщений: 889
По умолчанию

Цитата:
Сообщение от Aoizora Посмотреть сообщение
>А вот тут жутко черная магия

По-моему, это просто портянка копрокода. Из-за такого когда и говорят о том, что хаскель лучше крестов. А что насчет производительности такого кода по сравнению с аналогом на ФП-языках?
...это вы нифига не понимаете) - это ж мета-код, там будет большой (смотря скок уникальных ID) и очень быстрый код. Почти макросы, ток круче - 1 раз написал, а дальше все забота компилятора. А такая "портянка", что-бы как раз соотв. всем стандартам "правильных алгоритмов", его можно раза в 2 ужать и все равно будет работать почти всегда.

Ладно, я че, я нече ) - сам его откуда-то утянул. Но потратил целую ночь, чтобы выгуглить и хотя бы чуть-чуть оценить результаты по запросу "как передать лямбду, как указатель на С функцию". Но работает.

Последний раз редактировалось alexzk; 19.06.2017 в 16:58.
alexzk вне форума Ответить с цитированием
Старый 19.06.2017, 17:16   #8
waleri
Старожил
 
Регистрация: 13.07.2012
Сообщений: 6,493
По умолчанию

Цитата:
Сообщение от alexzk Посмотреть сообщение
"как передать лямбду, как указатель на С функцию"
std::function?
waleri вне форума Ответить с цитированием
Старый 20.06.2017, 05:04   #9
alexzk
Форумчанин
 
Регистрация: 12.04.2017
Сообщений: 889
По умолчанию

Цитата:
Сообщение от waleri Посмотреть сообщение
std::function?
ну попробуйте смысл в том, что не можем менять "приемник", т..к. это какая-то библиотека, которая хочет указатель. В итоге вы прийдете к вычурной статик-функции, которая будет принимать вашу лямбду + еще что-то, паковать в структуру и передавать в библиотеку + еще 1 статик...и т.д.
Вобщем темплейт выше все это и делает.

Последний раз редактировалось alexzk; 20.06.2017 в 05:07.
alexzk вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Ошибка при выполнении запроса dima_r SQL, базы данных 6 14.04.2011 17:47
Try и except при выполнении запроса с ошибкой Droid БД в Delphi 4 17.04.2010 19:10
Ошибка при выполнении запроса Шани БД в Delphi 4 27.07.2007 13:04
Ошибка при выполнении запроса Elena БД в Delphi 3 14.06.2007 15:13
Ошибка при выполнении запроса Elena БД в Delphi 2 25.05.2007 16:19