Здравствуйте, Gorilla, Вы писали:
PM>>В С++11 есть std::is_base_of и std::enable_if G>Тогда хорошо бы было добавить в проект соответствующий конвертор, поместив его внутри блока #if _MSC_VER >= 1600 / #endif
Не вижу в этом большой потребности, т.к. поддержка С++11 — отдельная тема. Вообще, поддержка наследников std::vector в конвертере данных это достаточно экзотический случай. Если вам необходимы конвертеры для собственных типов, сделайте в своем проекте специализацию sqlitepp::converter<> для них.
G>Кстати, в последней версии из svn возникает такой варнинг при компиляции: G>
G>cl : Command line warning D9030: '/Gm' is incompatible with multiprocessing; ignoring /MP switch
G>
G>И еще хорошо бы было обновить sqlite до версии 3.7.12.
Предупржедение починил, обновлять SQLite пока некогда.
Начал использовать SQLite++ (http://sqlitepp.berlios.de/) в своем проекте, возникло несколько вопросов и предложений по развитию проекта.
1 — Проект еще жив? Последний коммит был в 2007 году.
2 — Можно ли использовать один объект sqlitepp::session из нескольких потоков? Если да, то какие ограничения на такое использование?
3 — Кровь из носу нужна возможность вызывать сишные sqlite функции для объекта sqlitepp::session, например никак невозможно обойтись без sqlite3_busy_timeout, иначе запросы к базе обламываются при параллельном доступе из другого объекта sqlitepp::session.
4 — Нужна возможность указывать флаги при открытии базы, например нужен флаг SQLITE_OPEN_SHAREDCACHE, он ускоряет одновременную работу нескольких сессий БД.
Можно ли все это сделать? И вопрос к автору: если я сам это сделаю, примет ли он патч?
Отвечал на личное сообещние, может не дошло
G>Начал использовать SQLite++ (http://sqlitepp.berlios.de/) в своем проекте, возникло несколько вопросов и предложений по развитию проекта. G>1 — Проект еще жив? Последний коммит был в 2007 году.
Эээ, проект есть. Последний коммит в trunk был 6 апреля 2011 г.
G>2 — Можно ли использовать один объект sqlitepp::session из нескольких потоков? Если да, то какие ограничения на такое использование?
Можно, если обеспечить синхронизацию доступа к экземпляру sqlitepp::session, т.к. класс session не потокобезопасен.
G>3 — Кровь из носу нужна возможность вызывать сишные sqlite функции для объекта sqlitepp::session, например никак невозможно обойтись без sqlite3_busy_timeout, иначе запросы к базе обламываются при параллельном доступе из другого объекта sqlitepp::session.
Можно добавить нужные функции в класс sqlitepp::session, или сделать доступным sqlite3* хранящийся в session. Мне кажется первый путь чуть безопаснее и функции session::last_changes(), session::total_changes() были так и добавлены.
G>4 — Нужна возможность указывать флаги при открытии базы, например нужен флаг SQLITE_OPEN_SHAREDCACHE, он ускоряет одновременную работу нескольких сессий БД.
Без проблем — можно добавить параметр для флагов в session::open()
G>Можно ли все это сделать? И вопрос к автору: если я сам это сделаю, примет ли он патч?
Здравствуйте, PM, Вы писали:
PM>Можно добавить нужные функции в класс sqlitepp::session, или сделать доступным sqlite3* хранящийся в session. Мне кажется первый путь чуть безопаснее и функции session::last_changes(), session::total_changes() были так и добавлены.
При нужде не очень хочется менять библиотечные заголовочные файлы, ведь они могут быть перезаписаны при обновлении. Наверное, для таких случаев лучше предоставить явный доступ к экземпляру sqlite3.
Даже не знал про такую штуку.
В свое время написал свою обёртку с datasource, datarow, всякими перегруженными операторами преобразования к интегральным типам и std::s/w string, с поддержкой prepared statment-ов, с поддержкой сессий, и возможностью инжектить самописные extensions (т.е. расширять набор функций SQL). Вот уже год как юзаем, никто пока не жаловался
Здравствуйте, PM, Вы писали:
PM>Здравствуйте, Gorilla, Вы писали:
PM>Эээ, проект есть. Последний коммит в trunk был 6 апреля 2011 г.
Cорри, смотрел по дате на главной странице сайта. Раз проект жив, то не помешало бы обновить версию sqlite до 3.7.10 и в солюшене для VC10 прописать x64 конфигурацию.
PM>Можно, если обеспечить синхронизацию доступа к экземпляру sqlitepp::session, т.к. класс session не потокобезопасен.
Тогда сужаю вопрос: конструкция
unsigned long count;
database << "SELECT COUNT(*) FROM table", sqlitepp::into(count);
потокобезопасна? В коде много таких конструкций, очень не хочется здесь городить синхронизацию. Пока без нее все работает.
PM>Можно добавить нужные функции в класс sqlitepp::session, или сделать доступным sqlite3* хранящийся в session. Мне кажется первый путь чуть безопаснее и функции session::last_changes(), session::total_changes() были так и добавлены.
Хорошо бы тогда добавить нужные функции плюс функцию get_impl() для прямого доступа к sqlite3*. Все возможности sqlite сразу не перекроешь, желательно оставлять пользователям библиотеки обходной вариант на свой страх и риск, чтобы не пришлось делать #define private public перед хедером.
PM>Без проблем — можно добавить параметр для флагов в session::open()
Флаги уже не нужны, почитав документацию я нашел функцию sqlite3_enable_shared_cache включающую разделяемый кэш глобально. Мне этого достаточно.
PM>Патч приму
Отлично, в ближайшее время будет.
Здравствуйте, nen777w, Вы писали:
N>Даже не знал про такую штуку. N>В свое время написал свою обёртку с datasource, datarow, всякими перегруженными операторами преобразования к интегральным типам и std::s/w string, с поддержкой prepared statment-ов, с поддержкой сессий, и возможностью инжектить самописные extensions (т.е. расширять набор функций SQL). Вот уже год как юзаем, никто пока не жаловался
Я делал эту обертку на идеях из SOCI, которая тогда поддерживала только Oracle. Есть еще одна похожая библиотека, CppDB, использовал ее в одном проекте для работы с MySQL.
Здравствуйте, Gorilla, Вы писали:
G>Здравствуйте, PM, Вы писали:
PM>>Здравствуйте, Gorilla, Вы писали:
PM>>Эээ, проект есть. Последний коммит в trunk был 6 апреля 2011 г. G>Cорри, смотрел по дате на главной странице сайта. Раз проект жив, то не помешало бы обновить версию sqlite до 3.7.10 и в солюшене для VC10 прописать x64 конфигурацию.
Это может быть когда-нибудь SQLite можно обновлять по необходимости — достаточно распаковывать файлы из SQLite amalagamation в каталог sqlite и пересобирать библиотеку. В плане поддержки обратной совместимости D. Richard Hipp делает все что можно.
PM>>Можно, если обеспечить синхронизацию доступа к экземпляру sqlitepp::session, т.к. класс session не потокобезопасен. G>Тогда сужаю вопрос: конструкция G>
G>unsigned long count;
G>database << "SELECT COUNT(*) FROM table", sqlitepp::into(count);
G>
G>потокобезопасна? В коде много таких конструкций, очень не хочется здесь городить синхронизацию. Пока без нее все работает.
Везет В экземпляре session хранятся 2 вспомогательны переменных — указатель на активную транзакцию и результат последнего вызова sqlite3_step(). Я думаю, если не обращаться к session::active_txn() и всем функциям session::last_*() то все будет в порядке (при условии что экземпляр session живет дольше любого из созданных в нем statement).
PM>>Можно добавить нужные функции в класс sqlitepp::session, или сделать доступным sqlite3* хранящийся в session. Мне кажется первый путь чуть безопаснее и функции session::last_changes(), session::total_changes() были так и добавлены. G>Хорошо бы тогда добавить нужные функции плюс функцию get_impl() для прямого доступа к sqlite3*. Все возможности sqlite сразу не перекроешь, желательно оставлять пользователям библиотеки обходной вариант на свой страх и риск, чтобы не пришлось делать #define private public перед хедером.
Ок, не вижу проблем добавить пару функций sqlite3* session::impl() const и sqlite3_stmt* statement::impl() const;
Здравствуйте, Gorilla, Вы писали:
G>3 — Кровь из носу нужна возможность вызывать сишные sqlite функции для объекта sqlitepp::session, например никак невозможно обойтись без sqlite3_busy_timeout, иначе запросы к базе обламываются при параллельном доступе из другого объекта sqlitepp::session.
Здравствуйте, PM, Вы писали:
PM>Здравствуйте, Gorilla, Вы писали:
G>>Я еще добавил вот такой патч:
PM>[здесь был патч]
PM>спасибо, применю. Только я думаю не добавлять третий конструктор session, а добавить к нему второму параметр:
PM>
PM>explicit session(string_t const& filename, int flags = 0);
PM>
Функция open_v2 есть только в utf8 варианте, это может стать проблемой.
PM>>explicit session(string_t const& filename, int flags = 0);
PM>>
G>Функция open_v2 есть только в utf8 варианте, это может стать проблемой.
Это не проблема В sqlitepp/string.hpp есть функция utf8() для конверсии строк в UTF-8
Закоммитил в trunk.
Вчера внезапно нашлось время для обновления SQLite до версии 3.7.10 Заодно добавил функции session::impl() и statement::impl() для прямого доступа к структурам SQLite. Также обновил Solution для Visual Studio 2010 — добавил конфигурацию для x64, переименовал targets сборки чтобы не было конфликтов при сборке Win32 или x64. Правда batch build не проходит. Но по отдельности все цели собираются и тесты проходят.
Еще небольшой список изменений которых не хватает для полного счасться:
Вот мои правки последней версии проекта, с ними он красивее компилируется и включена вся нужная в релизе оптимизация.
Еще наткнулся на баг студии с созданием пусорной директории "Debug", но не знаю как его обойти. Может быть стоит компилить исходники sqlite внутри проекта sqlitepp, а не в отдельном проекте?
Здравствуйте, PM, Вы писали:
PM>Применил, спасибо. Также подчистил немного код и настройки тестового проекта чтобы избавиться от предупреждений компилятора.
А зачем удалил "<Filter Include="Source Files">..." из файлов .filters? Ведь именно в этом фича фильтров, чтобы показывало хедеры и сорсы отдельно.
Здравствуйте, Gorilla, Вы писали:
G>Еще небольшой список изменений которых не хватает для полного счасться:
G>Вот мои правки последней версии проекта, с ними он красивее компилируется и включена вся нужная в релизе оптимизация. G>Еще наткнулся на баг студии с созданием пусорной директории "Debug", но не знаю как его обойти. Может быть стоит компилить исходники sqlite внутри проекта sqlitepp, а не в отдельном проекте?
Применил, спасибо. Также подчистил немного код и настройки тестового проекта чтобы избавиться от предупреждений компилятора.
Насчет исходников SQLite внутри SQLite++ не уверен — я собираю sqltepp.so в Linux, и там почти всегда уже есть какая-то из более-менее свежих версий SQLite. Я думаю зависимости от SQLite на уровне библиотек вполне достаточно.
Здравствуйте, Gorilla, Вы писали:
G>А зачем удалил "<Filter Include="Source Files">..." из файлов .filters? Ведь именно в этом фича фильтров, чтобы показывало хедеры и сорсы отдельно.
Никогда не понимал искусственное деление на Source/Header. Предпочитаю в своих проектах группировать файлы по подсистемам. На мой взгляд так удобнее и логичнее, когда заголовочный и исходный файлы отображаются рядом в Solution Explorer. И так быстрее между ними переключаться. В идеале, иерархия файлов в Solution Explorer и их размещение в файловой системе совпадают.
Под капотом вызывается sqlite3_bind_text(impl_, pos, str, -1, SQLITE_STATIC) или sqlite3_bind_text16 для utf16_char, т.е. преборазований в string_t и лишних копирований не происходит.
Здравствуйте, PM, Вы писали:
PM>Здравствуйте, Gorilla, Вы писали:
G>>А можно добавить use биндеры для char* и wchar_t* ?
PM>Добавил пару перегруженных функций: PM>
Вопрос: какова область видимости блоба получаемого через into? Нужно ли как-то освобождать память под него?
Например:
sqlitepp::session db("test.db");
sqlitepp::blob icon;
db << "SELECT data FROM incons WHERE id=1234", sqlitepp::into(icon);
// можно ли обращаться к icon.data? нужно ли освобождать память?
Далее: у меня есть класс производный от std:vector<unsigned char>, но into и use биндеры не принимают его напрямую, требуется некрасивое приведение типов. Можно ли заставить биндеры принимать любые производные вектора?
Еще одно пожелание: когда я отправляю селект через << на sqlitepp::session, мне часто требуется писать if (!db.last_exec()) throw std::runtime_error("no data selected"). Может можно сделать какую-нибудь опцию при открытии базы, или сделать какой-нибудь модификатор вроде db << "sql query", sqlitepp::throw(), или метод db.set_throw_if_no_data(true) который включает выбрасывание исключения если запрос имел into биндеры и не вернул никаких данных. Даже не знаю как лучше.
В добавок одно наблюдения от меня, которое сэкономит вам кучу времени и нервных клеток. Функция sqlite3_enable_shared_cache делает работу с БД потоконебезопасной!!! Shared caсhe у них баговый и может вызывать отказ при обработке параллельных запросов. В документации про это ничего не сказано, остерегайтесь использования этой функции.
Здравствуйте, Gorilla, Вы писали:
G>Вопрос: какова область видимости блоба получаемого через into? Нужно ли как-то освобождать память под него?
The pointers returned are valid until a type conversion occurs as described above, or until sqlite3_step() or sqlite3_reset() or sqlite3_finalize() is called. The memory space used to hold strings and BLOBs is freed automatically. Do not pass the pointers returned sqlite3_column_blob(), sqlite3_column_text(), etc. into sqlite3_free().
G>// можно ли обращаться к icon.data? нужно ли освобождать память?
Нельзя, не нужно. Чтобы обратиться к полученным данным, нужно либо подержать объект sqlitepp::statement до момента обращения к данным, либо писать свой конвертер, чтобы управлять памятью по-своему.
G>Далее: у меня есть класс производный от std:vector<unsigned char>, но into и use биндеры не принимают его напрямую, требуется некрасивое приведение типов. Можно ли заставить биндеры принимать любые производные вектора?
Напишите конвертер с красивым приведением
Может ли запрос дождаться освобождения базы, а не вылетать с SQLITE_BUSY? Есть ли у sqlite встроенные средства для этого или надо организовывать самописную синхронизацию?
Тем более, что база может использоваться из разных приложений, к примеру.
Здравствуйте, ntp, Вы писали:
ntp>Здравствуйте, Gorilla, Вы писали:
G>>Вопрос: какова область видимости блоба получаемого через into? Нужно ли как-то освобождать память под него?
ntp>
The pointers returned are valid until a type conversion occurs as described above, or until sqlite3_step() or sqlite3_reset() or sqlite3_finalize() is called. The memory space used to hold strings and BLOBs is freed automatically. Do not pass the pointers returned sqlite3_column_blob(), sqlite3_column_text(), etc. into sqlite3_free().
G>>// можно ли обращаться к icon.data? нужно ли освобождать память? ntp>Нельзя, не нужно. Чтобы обратиться к полученным данным, нужно либо подержать объект sqlitepp::statement до момента обращения к данным, либо писать свой конвертер, чтобы управлять памятью по-своему.
Я так и предполагал, но на всякий случай спрашиваю, вдруг там statement живет до следующего запроса. Пофиг, буду грузить в вектор.
G>>Далее: у меня есть класс производный от std:vector<unsigned char>, но into и use биндеры не принимают его напрямую, требуется некрасивое приведение типов. Можно ли заставить биндеры принимать любые производные вектора? ntp>Напишите конвертер с красивым приведением
К сожалению я не настолько продвинут в с++ чтобы лезть в такие дебри.
ntp>Может ли запрос дождаться освобождения базы, а не вылетать с SQLITE_BUSY? Есть ли у sqlite встроенные средства для этого или надо организовывать самописную синхронизацию? ntp>Тем более, что база может использоваться из разных приложений, к примеру.
sqlite3_busy_timeout(database->impl(), 10*1000); — устанавливаем таймаут 10сек.
Здравствуйте, Gorilla, Вы писали:
G>>>Далее: у меня есть класс производный от std:vector<unsigned char>, но into и use биндеры не принимают его напрямую, требуется некрасивое приведение типов. Можно ли заставить биндеры принимать любые производные вектора? ntp>>Напишите конвертер с красивым приведением G>К сожалению я не настолько продвинут в с++ чтобы лезть в такие дебри.
Если нельзя привести ваш класс к базовому (и уже его передать в into/use), то тогда вот пример, в который можно подставить любой объект, лишь бы его данные, захваченные blob'ом, можно было описать непрерывным куском памяти:
Здравствуйте, ntp, Вы писали:
ntp>Если нельзя привести ваш класс к базовому (и уже его передать в into/use)
Можно привести, я сейчас так и делаю. Но это некрасиво, хотелось бы избавиться от длинного приведения, чтобы оно делалось автоматом в конвертере, и чтобы работало для любого класса производного от vector. Такое возможно?
Здравствуйте, Gorilla, Вы писали:
G>Здравствуйте, ntp, Вы писали:
ntp>>Если нельзя привести ваш класс к базовому (и уже его передать в into/use) G>Можно привести, я сейчас так и делаю. Но это некрасиво, хотелось бы избавиться от длинного приведения, чтобы оно делалось автоматом в конвертере, и чтобы работало для любого класса производного от vector. Такое возможно?
Если я правильно понял суть вопроса, то можно выкрутиться так:
#include <boost/type_traits/is_base_of.hpp>
#include <boost/utility/enable_if.hpp>
// Конвертер для классов унаследованных от std::vector<T>. Тип VectorT должен обеспечить конструктор из диапазона итераторов:
//
// template<typename T>
// struct VectorT : public std::vector<T>
// {
// /// Конструктор из диапазона итераторов, в частном случае это может быть [value_type const* begin, value_type const* end)
// template<typename ForwardIterator>
// VectorT(ForwardIterator begin, ForwardIterator end) : std::vector<T>(begin, end) {}
// };
//template<typename VectorT>
struct converter<VectorT,
typename boost::enable_if<boost::is_base_of<std::vector<typename VectorT::value_type>, VectorT> >::type>
{
typedef blob base_type;
static VectorT to(blob const& b)
{
typename VectorT::value_type const* f = reinterpret_cast<typename VectorT::value_type const*>(b.data);
typename VectorT::value_type const* l = f + b.size / sizeof(typename VectorT::value_type);
return VectorT(f, l);
}
static blob from(VectorT const& t)
{
blob b;
b.data = t.empty()? 0 : &t[0];
b.size = t.size() * sizeof(typename VectorT::value_type);
return b;
}
};
И тогда такое будет работать:
template<typename T>
struct my_vector : public std::vector<T>
{
my_vector() {}
template<typename ForwardIt>
my_vector(ForwardIt begin, ForwardIt end)
: std::vector<T>(begin, end)
{
}
};
void test()
{
my_vector<int> data, data2;
data.push_back(1);
data.push_back(11);
using namespace sqlitepp;
session se("test.db");
se << utf(L"create table my_vector(val blob)");
se << utf(L"insert into my_vector(val) values(:val)"), use(data);
se << utf(L"select val from my_vector"), into(data2);
ensure("my_vector", data2 == data);
}
PM>Если я правильно понял суть вопроса, то можно выкрутиться так:
А можно то-же самое, но без boost? Одна из причин по которой я выбрал sqlitepp — это отсутствие зависимостей от boost.
Здравствуйте, Gorilla, Вы писали:
G>А можно то-же самое, но без boost? Одна из причин по которой я выбрал sqlitepp — это отсутствие зависимостей от boost.
В С++11 есть std::is_base_of и std::enable_if
Можно реализовать их самому, либо посмотреть как сделано в Boost и реализовать самому.
Здравствуйте, PM, Вы писали:
PM>Здравствуйте, Gorilla, Вы писали:
G>>А можно то-же самое, но без boost? Одна из причин по которой я выбрал sqlitepp — это отсутствие зависимостей от boost.
PM>В С++11 есть std::is_base_of и std::enable_if
Тогда хорошо бы было добавить в проект соответствующий конвертор, поместив его внутри блока #if _MSC_VER >= 1600 / #endif
Кстати, в последней версии из svn возникает такой варнинг при компиляции:
cl : Command line warning D9030: '/Gm' is incompatible with multiprocessing; ignoring /MP switch
И еще хорошо бы было обновить sqlite до версии 3.7.12.
Вопрос к PM: не планируется ли портировать библиотеку под mysql? Несмотря на все ухищрения мой проект перерос sqlite, требуется взрослая база данных. Но я так привык к sqlite++, что другие обертки кажутся ужасными.
Здравствуйте, Gorilla, Вы писали:
G>Вопрос к PM: не планируется ли портировать библиотеку под mysql? Несмотря на все ухищрения мой проект перерос sqlite, требуется взрослая база данных. Но я так привык к sqlite++, что другие обертки кажутся ужасными.
Довольно странно в качестве примера взрослой базы данных приводить mysql. А у Postgres’а есть вполне modern C++-style обёртка libpqxx.
Здравствуйте, Gorilla, Вы писали:
G>Вопрос к PM: не планируется ли портировать библиотеку под mysql? Несмотря на все ухищрения мой проект перерос sqlite, требуется взрослая база данных. Но я так привык к sqlite++, что другие обертки кажутся ужасными.
Нет, я не планирую добавлять в SQlite++ поддержку других БД. По-моему, нет необъодимости в еще одной универсальной БД библиотеке
На вскидку могу вспомнить SOCI и CppDB (последнюю кстати я успешно использовал в одном проекте c MySQL)