├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── Doxyfile.in ├── README.md ├── adioConfig.in.cmake ├── cmake ├── ImportBoost.cmake └── ImportSQLite.cmake ├── docs ├── cppreference.tag.xml ├── customdoxygen.css ├── doxy-boot.js ├── footer.html ├── header.html └── style.css ├── source ├── CMakeLists.txt ├── adio │ ├── config.hpp │ ├── connection.cpp │ ├── connection.hpp │ ├── connection_fwd.hpp │ ├── docs.hpp │ ├── error.cpp │ ├── error.hpp │ ├── service.hpp │ ├── sql │ │ ├── row.hpp │ │ ├── value.cpp │ │ └── value.hpp │ ├── traits.hpp │ └── utils.hpp └── drivers │ ├── CMakeLists.txt │ ├── empty │ ├── CMakeLists.txt │ └── adio │ │ ├── empty.cpp │ │ └── empty.hpp │ ├── postgresql │ ├── CMakeLists.txt │ └── adio │ │ ├── postgresql.cpp │ │ └── postgresql.hpp │ └── sqlite │ ├── CMakeLists.txt │ └── adio │ ├── sqlite.cpp │ └── sqlite.hpp └── tests ├── CMakeLists.txt ├── connection.cpp ├── empty.cpp ├── postgresql.cpp ├── row.cpp ├── sqlite.cpp └── value.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | *.user* 2 | build/ 3 | *.sw? 4 | *.sw?? 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | compiler: 3 | - gcc 4 | - clang 5 | 6 | sudo: required 7 | dist: trusty 8 | 9 | cache: 10 | - apt 11 | 12 | env: 13 | - BT=Release 14 | - BT=Debug 15 | 16 | before_install: 17 | - sudo apt-get -y update -qq 18 | 19 | install: 20 | - curl -sL -o cmake.sh https://cmake.org/files/v3.5/cmake-3.5.0-rc1-Linux-x86_64.sh 21 | - sudo sh cmake.sh --prefix=/usr/local --exclude-subdir 22 | - sudo apt-get -y install boost1.54 libsqlite3-dev 23 | 24 | 25 | script: 26 | - | 27 | set -eu 28 | cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=${BT} 29 | cmake --build build 30 | cmake -E chdir build ctest -T Test 31 | cmake -E chdir build cpack 32 | set +u 33 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0.0) 2 | project(adio VERSION 0.0.0 LANGUAGES CXX) 3 | 4 | set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE) 5 | set(CMAKE_INCLUDE_CURRENT_DIR TRUE) 6 | 7 | include(CTest) 8 | enable_testing() 9 | 10 | find_package(Pew QUIET) 11 | include(CMakePackageConfigHelpers) 12 | 13 | 14 | if(Pew_FOUND) 15 | find_package(boost QUIET) 16 | find_package(asio QUIET) 17 | find_package(sqlite REQUIRED) 18 | else() 19 | include(cmake/ImportBoost.cmake) 20 | include(cmake/ImportSQLite.cmake) 21 | find_package(Boost QUIET) 22 | endif() 23 | 24 | if(NOT boost_FOUND AND NOT Boost_FOUND) 25 | set(have_boost NO) 26 | find_package(asio QUIET) 27 | set(default_backend asio) 28 | else() 29 | set(have_boost TRUE) 30 | set(default_backend boost) 31 | endif() 32 | 33 | set(can_build TRUE) 34 | if(NOT have_boost AND NOT asio_FOUND) 35 | message(SEND_ERROR "Cannot build tests/examples without either Boost or asio installed") 36 | endif() 37 | 38 | set(ASIO_BACKEND "${default_backend}" CACHE STRING "Choose the asio type to use (boost/vanilla)") 39 | set_property(CACHE ASIO_BACKEND PROPERTY STRINGS boost vanilla) 40 | message(STATUS "Targeting the \"${ASIO_BACKEND}\" asio backend") 41 | 42 | if(ASIO_BACKEND STREQUAL vanilla) 43 | find_package(asio REQUIRED) 44 | endif() 45 | 46 | if(WIN32) 47 | set(ADIO_CMAKE_PREFIX cmake) 48 | else() 49 | set(ADIO_CMAKE_PREFIX ${PROJECT_NAME}-${PROJECT_VERSION}/cmake) 50 | endif() 51 | 52 | add_subdirectory(source) 53 | if(BUILD_TESTING) 54 | find_package(catch) 55 | if(NOT catch_FOUND) 56 | message(WARNING "Cannot build the tests without Catch") 57 | else() 58 | add_subdirectory(tests) 59 | endif() 60 | endif() 61 | 62 | if(Pew_FOUND) 63 | pew_export_project() 64 | else() 65 | install(EXPORT adioTargets DESTINATION ${ADIO_CMAKE_PREFIX}) 66 | write_basic_package_version_file(${PROJECT_NAME}ConfigVersion.cmake COMPATIBILITY SameMajorVersion) 67 | configure_package_config_file(${PROJECT_NAME}Config.in.cmake ${PROJECT_NAME}Config.cmake INSTALL_DESTINATION ${ADIO_CMAKE_PREFIX}) 68 | install( 69 | FILES 70 | ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake 71 | ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake 72 | DESTINATION ${ADIO_CMAKE_PREFIX} 73 | ) 74 | endif() 75 | 76 | find_program(DOXYGEN_EXECUTABLE doxygen DOC "Path to Doxygen, for generating documentation") 77 | set(doxy_js ${CMAKE_CURRENT_LIST_DIR}/docs/doxy-boot.js) 78 | set(doxy_css ${CMAKE_CURRENT_LIST_DIR}/docs/customdoxygen.css) 79 | if(DOXYGEN_EXECUTABLE AND EXISTS "${doxy_js}") 80 | configure_file(Doxyfile.in Doxyfile @ONLY) 81 | file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/docs) 82 | add_custom_target(docs 83 | COMMAND "${DOXYGEN_EXECUTABLE}" "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile" 84 | #COMMAND "${CMAKE_COMMAND}" -E copy "${doxy_css}" "${CMAKE_CURRENT_BINARY_DIR}/docs/html" 85 | COMMAND "${CMAKE_COMMAND}" -E copy "${doxy_js}" "${CMAKE_CURRENT_BINARY_DIR}/docs/html" 86 | COMMENT "Generating documentation" 87 | WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/docs" 88 | USES_TERMINAL 89 | ) 90 | endif() 91 | 92 | set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION}) 93 | set(CPACK_OUTPUT_FILE_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/packages) 94 | include(CPack) 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Adio 2 | ## Asynchronous Database Library based on Asio 3 | 4 | Adio is a database abstraction library built on top of Asio. Adio code may look 5 | very similar to code in Asio, and it follows the same patterns and styles found 6 | in Asio. For example, to open a database connection synchronously: 7 | 8 | ~~~cpp 9 | #include 10 | 11 | int main() 12 | { 13 | adio::io_service ios; 14 | adio::sqlite::connection con{ ios }; 15 | const auto ec = con.open("database.db"); 16 | if (!ec) 17 | std::cout << "Database was opened!\n"; 18 | else 19 | std::cout << "There was an error!\n"; 20 | } 21 | ~~~ 22 | 23 | And asynchronously: 24 | 25 | ~~~cpp 26 | #include 27 | 28 | int main() 29 | { 30 | adio::io_service ios; 31 | adio::sqlite::connection con{ ios }; 32 | con.async_open("database.db", [&con](const auto ec) { 33 | if (!ec) 34 | std::cout << "Database was opened!\n"; 35 | else 36 | std::cout << "There was an error!\n"; 37 | }); 38 | ios.run(); 39 | } 40 | ~~~ 41 | 42 | \* Note that ``adio::io_service`` is simply an alias to ``asio::io_service`` or 43 | ``boost::asio::io_service``, depending on the Asio backend selected. 44 | 45 | ## Important note: 46 | 47 | Adio is very much in early developement. Feedback would be much appreciated! -------------------------------------------------------------------------------- /adioConfig.in.cmake: -------------------------------------------------------------------------------- 1 | include(adioTargets.cmake) 2 | -------------------------------------------------------------------------------- /cmake/ImportBoost.cmake: -------------------------------------------------------------------------------- 1 | set(Boost_USE_STATIC_LIBS TRUE) 2 | set(components system coroutine context thread) 3 | find_package(Boost REQUIRED ${components}) 4 | 5 | add_library(boost::boost INTERFACE IMPORTED) 6 | set_target_properties(boost::boost PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${Boost_INCLUDE_DIR}) 7 | 8 | foreach(lib IN LISTS components) 9 | add_library(boost::${lib} STATIC IMPORTED) 10 | string(TOUPPER "${lib}" ulib) 11 | set_target_properties(boost::${lib} PROPERTIES 12 | IMPORTED_LOCATION ${Boost_${ulib}_LIBRARY_RELEASE} 13 | IMPORTED_LOCATION_RELEASE ${Boost_${ulib}_LIBRARY_RELEASE} 14 | IMPORTED_LOCATION_DEBUG ${Boost_${ulib}_LIBRARY_DEBUG} 15 | INTERFACE_LINK_LIBRARIES boost::boost 16 | ) 17 | endforeach() 18 | 19 | set_property(TARGET boost::coroutine APPEND PROPERTY INTERFACE_LINK_LIBRARIES boost::context) 20 | 21 | add_library(boost::variant INTERFACE IMPORTED) 22 | set_target_properties(boost::variant PROPERTIES INTERFACE_LINK_LIBRARIES boost::boost) 23 | set_property(TARGET boost::variant PROPERTY INTERFACE_LINK_LIBRARIES boost::boost) 24 | add_library(boost::asio INTERFACE IMPORTED) 25 | set_property(TARGET boost::variant PROPERTY INTERFACE_LINK_LIBRARIES boost::boost boost::system boost::coroutine boost::thread) 26 | -------------------------------------------------------------------------------- /cmake/ImportSQLite.cmake: -------------------------------------------------------------------------------- 1 | find_library(SQLITE_LIBRARY NAMES libsqlite3.a sqlite3.lib libsqlite3.lib) 2 | find_path(SQLITE_DIR sqlite3.h) 3 | 4 | if(NOT SQLITE_LIBRARY) 5 | message(FATAL_ERROR "Cannot find SQLite") 6 | endif() 7 | 8 | add_library(sqlite::sqlite3 STATIC IMPORTED) 9 | set_target_properties( 10 | sqlite::sqlite3 PROPERTIES 11 | IMPORTED_LOCATION "${SQLITE_LIBRARY}" 12 | INTERFACE_INCLUDE_DIRECTORIES "${SQLITE_DIR}" 13 | ) 14 | 15 | if(NOT WIN32) 16 | set_property(TARGET sqlite::sqlite3 APPEND PROPERTY INTERFACE_LINK_LIBRARIES dl) 17 | endif() 18 | -------------------------------------------------------------------------------- /docs/customdoxygen.css: -------------------------------------------------------------------------------- 1 | h1, .h1, h2, .h2, h3, .h3{ 2 | font-weight: 200 !important; 3 | } 4 | 5 | #navrow1, #navrow2, #navrow3, #navrow4, #navrow5{ 6 | border-bottom: 1px solid #EEEEEE; 7 | } 8 | 9 | .adjust-right { 10 | margin-left: 30px !important; 11 | font-size: 1.15em !important; 12 | } 13 | .navbar{ 14 | border: 0px solid #222 !important; 15 | } 16 | 17 | div.contents > table tr.active, 18 | table.memname, 19 | div.memtemplate, 20 | div.contents h3, 21 | div div div.headertitle.page-header div.title.h1, 22 | td.paramname { 23 | font-family: monospace, Ubuntu Mono, Courier New; 24 | } 25 | 26 | span.label-info { 27 | margin: 5px; 28 | } 29 | 30 | div.contents div.panel { 31 | box-shadow: 0px 1px 2px gray; 32 | transition: 200ms box-shadow; 33 | } 34 | 35 | div.contents div.panel:hover { 36 | box-shadow: 1px 4px 10px gray; 37 | } 38 | 39 | 40 | /* Sticky footer styles 41 | -------------------------------------------------- */ 42 | html, 43 | body { 44 | height: 100%; 45 | /* The html and body elements cannot have any padding or margin. */ 46 | } 47 | 48 | /* Wrapper for page content to push down footer */ 49 | #wrap { 50 | min-height: 100%; 51 | height: auto; 52 | /* Negative indent footer by its height */ 53 | margin: 0 auto -60px; 54 | /* Pad bottom by footer height */ 55 | padding: 0 0 60px; 56 | } 57 | 58 | /* Set the fixed height of the footer here */ 59 | #footer { 60 | font-size: 0.9em; 61 | padding: 8px 0px; 62 | background-color: #f5f5f5; 63 | } 64 | 65 | .footer-row { 66 | line-height: 44px; 67 | } 68 | 69 | #footer > .container { 70 | padding-left: 15px; 71 | padding-right: 15px; 72 | } 73 | 74 | .footer-follow-icon { 75 | margin-left: 3px; 76 | text-decoration: none !important; 77 | } 78 | 79 | .footer-follow-icon img { 80 | width: 20px; 81 | } 82 | 83 | .footer-link { 84 | padding-top: 5px; 85 | display: inline-block; 86 | color: #999999; 87 | text-decoration: none; 88 | } 89 | 90 | .footer-copyright { 91 | text-align: center; 92 | } 93 | 94 | 95 | @media (min-width: 992px) { 96 | .footer-row { 97 | text-align: left; 98 | } 99 | 100 | .footer-icons { 101 | text-align: right; 102 | } 103 | } 104 | @media (max-width: 991px) { 105 | .footer-row { 106 | text-align: center; 107 | } 108 | 109 | .footer-icons { 110 | text-align: center; 111 | } 112 | } 113 | 114 | /* DOXYGEN Code Styles 115 | ----------------------------------- */ 116 | 117 | 118 | a.qindex { 119 | font-weight: bold; 120 | } 121 | 122 | a.qindexHL { 123 | font-weight: bold; 124 | background-color: #9CAFD4; 125 | color: #ffffff; 126 | border: 1px double #869DCA; 127 | } 128 | 129 | .contents a.qindexHL:visited { 130 | color: #ffffff; 131 | } 132 | 133 | a.code, a.code:visited, a.line, a.line:visited { 134 | color: #4665A2; 135 | } 136 | 137 | a.codeRef, a.codeRef:visited, a.lineRef, a.lineRef:visited { 138 | color: #4665A2; 139 | } 140 | 141 | /* @end */ 142 | 143 | dl.el { 144 | margin-left: -1cm; 145 | } 146 | 147 | pre.fragment { 148 | border: 1px solid #C4CFE5; 149 | background-color: #FBFCFD; 150 | padding: 4px 6px; 151 | margin: 4px 8px 4px 2px; 152 | overflow: auto; 153 | word-wrap: break-word; 154 | font-size: 9pt; 155 | line-height: 125%; 156 | font-family: monospace, fixed; 157 | font-size: 105%; 158 | } 159 | 160 | div.fragment { 161 | padding: 4px 6px; 162 | margin: 4px 8px 4px 2px; 163 | border: 1px solid #C4CFE5; 164 | } 165 | 166 | div.line { 167 | font-family: monospace, fixed; 168 | font-size: 13px; 169 | min-height: 13px; 170 | line-height: 1.0; 171 | text-wrap: unrestricted; 172 | white-space: -moz-pre-wrap; /* Moz */ 173 | white-space: -pre-wrap; /* Opera 4-6 */ 174 | white-space: -o-pre-wrap; /* Opera 7 */ 175 | white-space: pre-wrap; /* CSS3 */ 176 | word-wrap: break-word; /* IE 5.5+ */ 177 | text-indent: -53px; 178 | padding-left: 53px; 179 | padding-bottom: 0px; 180 | margin: 0px; 181 | -webkit-transition-property: background-color, box-shadow; 182 | -webkit-transition-duration: 0.5s; 183 | -moz-transition-property: background-color, box-shadow; 184 | -moz-transition-duration: 0.5s; 185 | -ms-transition-property: background-color, box-shadow; 186 | -ms-transition-duration: 0.5s; 187 | -o-transition-property: background-color, box-shadow; 188 | -o-transition-duration: 0.5s; 189 | transition-property: background-color, box-shadow; 190 | transition-duration: 0.5s; 191 | } 192 | 193 | div.line.glow { 194 | background-color: cyan; 195 | box-shadow: 0 0 10px cyan; 196 | } 197 | 198 | 199 | span.lineno { 200 | padding-right: 4px; 201 | text-align: right; 202 | border-right: 2px solid #0F0; 203 | background-color: #E8E8E8; 204 | white-space: pre; 205 | } 206 | span.lineno a { 207 | background-color: #D8D8D8; 208 | } 209 | 210 | span.lineno a:hover { 211 | background-color: #C8C8C8; 212 | } 213 | 214 | div.groupHeader { 215 | margin-left: 16px; 216 | margin-top: 12px; 217 | font-weight: bold; 218 | } 219 | 220 | div.groupText { 221 | margin-left: 16px; 222 | font-style: italic; 223 | } 224 | 225 | /* @group Code Colorization */ 226 | 227 | span.keyword { 228 | color: #008000 229 | } 230 | 231 | span.keywordtype { 232 | color: #604020 233 | } 234 | 235 | span.keywordflow { 236 | color: #e08000 237 | } 238 | 239 | span.comment { 240 | color: #800000 241 | } 242 | 243 | span.preprocessor { 244 | color: #806020 245 | } 246 | 247 | span.stringliteral { 248 | color: #002080 249 | } 250 | 251 | span.charliteral { 252 | color: #008080 253 | } 254 | 255 | span.vhdldigit { 256 | color: #ff00ff 257 | } 258 | 259 | span.vhdlchar { 260 | color: #000000 261 | } 262 | 263 | span.vhdlkeyword { 264 | color: #700070 265 | } 266 | 267 | span.vhdllogic { 268 | color: #ff0000 269 | } 270 | 271 | blockquote { 272 | background-color: #F7F8FB; 273 | border-left: 2px solid #9CAFD4; 274 | margin: 0 24px 0 4px; 275 | padding: 0 12px 0 16px; 276 | } 277 | 278 | -------------------------------------------------------------------------------- /docs/doxy-boot.js: -------------------------------------------------------------------------------- 1 | $( document ).ready(function() { 2 | 3 | $("div.headertitle").addClass("page-header"); 4 | $("div.title").addClass("h1"); 5 | 6 | $('li > a[href="index.html"] > span').before(" "); 7 | $('li > a[href="index.html"] > span').text("NeoStreams"); 8 | $('li > a[href="modules.html"] > span').before(" "); 9 | $('li > a[href="namespaces.html"] > span').before(" "); 10 | $('li > a[href="annotated.html"] > span').before(" "); 11 | $('li > a[href="classes.html"] > span').before(" "); 12 | $('li > a[href="inherits.html"] > span').before(" "); 13 | $('li > a[href="functions.html"] > span').before(" "); 14 | $('li > a[href="functions_func.html"] > span').before(" "); 15 | $('li > a[href="functions_vars.html"] > span').before(" "); 16 | $('li > a[href="functions_enum.html"] > span').before(" "); 17 | $('li > a[href="functions_eval.html"] > span').before(" "); 18 | $('img[src="ftv2ns.png"]').replaceWith('N '); 19 | $('img[src="ftv2cl.png"]').replaceWith('C '); 20 | 21 | $("ul.tablist").addClass("nav nav-pills nav-justified"); 22 | $("ul.tablist").css("margin-top", "0.5em"); 23 | $("ul.tablist").css("margin-bottom", "0.5em"); 24 | $("li.current").addClass("active"); 25 | $("iframe").attr("scrolling", "yes"); 26 | 27 | $("#nav-path > ul").addClass("breadcrumb"); 28 | 29 | $("table.params").addClass("table"); 30 | $("div.ingroups").wrapInner(""); 31 | $("div.levels").css("margin", "0.5em"); 32 | $("div.levels > span").addClass("btn btn-default btn-xs"); 33 | $("div.levels > span").css("margin-right", "0.25em"); 34 | 35 | $("table.directory").addClass("table table-striped"); 36 | $("div.summary > a").addClass("btn btn-default btn-xs"); 37 | $("table.fieldtable").addClass("table"); 38 | $(".fragment").addClass("well"); 39 | $(".memitem").addClass("panel panel-default"); 40 | $(".memproto").addClass("panel-heading"); 41 | $(".memdoc").addClass("panel-body"); 42 | $("span.mlabel").addClass("label label-info"); 43 | 44 | $("table.memberdecls").addClass("table"); 45 | $("[class^=memitem]").addClass("active"); 46 | 47 | $("div.ah").addClass("btn btn-default"); 48 | $("span.mlabels").addClass("pull-right"); 49 | $("table.mlabels").css("width", "100%") 50 | $("td.mlabels-right").addClass("pull-right"); 51 | 52 | $("div.ttc").addClass("panel panel-primary"); 53 | $("div.ttname").addClass("panel-heading"); 54 | $("div.ttname a").css("color", 'white'); 55 | $("div.ttdef,div.ttdoc,div.ttdeci").addClass("panel-body"); 56 | 57 | $('#MSearchBox').parent().remove(); 58 | 59 | $('div.fragment.well div.line:first').css('margin-top', '15px'); 60 | $('div.fragment.well div.line:last').css('margin-bottom', '15px'); 61 | 62 | $('table.doxtable').removeClass('doxtable').addClass('table table-striped table-bordered').each(function(){ 63 | $(this).prepend(''); 64 | $(this).find('tbody > tr:first').prependTo($(this).find('thead')); 65 | 66 | $(this).find('td > span.success').parent().addClass('success'); 67 | $(this).find('td > span.warning').parent().addClass('warning'); 68 | $(this).find('td > span.danger').parent().addClass('danger'); 69 | }); 70 | 71 | 72 | 73 | if($('div.fragment.well div.ttc').length > 0) 74 | { 75 | $('div.fragment.well div.line:first').parent().removeClass('fragment well'); 76 | } 77 | 78 | $('table.memberdecls').find('.memItemRight').each(function(){ 79 | $(this).contents().appendTo($(this).siblings('.memItemLeft')); 80 | $(this).siblings('.memItemLeft').attr('align', 'left'); 81 | }); 82 | 83 | function getOriginalWidthOfImg(img_element) { 84 | var t = new Image(); 85 | t.src = (img_element.getAttribute ? img_element.getAttribute("src") : false) || img_element.src; 86 | return t.width; 87 | } 88 | 89 | $('div.dyncontent').find('img').each(function(){ 90 | if(getOriginalWidthOfImg($(this)[0]) > $('#content>div.container').width()) 91 | $(this).css('width', '100%'); 92 | }); 93 | 94 | $(".memitem").removeClass('memitem'); 95 | $(".memproto").removeClass('memproto'); 96 | $(".memdoc").removeClass('memdoc'); 97 | $("span.mlabel").removeClass('mlabel'); 98 | $("table.memberdecls").removeClass('memberdecls'); 99 | $("[class^=memitem]").removeClass('memitem'); 100 | $("span.mlabels").removeClass('mlabels'); 101 | $("table.mlabels").removeClass('mlabels'); 102 | $("td.mlabels-right").removeClass('mlabels-right'); 103 | $(".navpath").removeClass('navpath'); 104 | $("li.navelem").removeClass('navelem'); 105 | $("a.el").removeClass('el'); 106 | $("div.ah").removeClass('ah'); 107 | $("div.header").removeClass("header"); 108 | 109 | $('.mdescLeft').each(function(){ 110 | if($(this).html()==" ") { 111 | $(this).siblings('.mdescRight').attr('colspan', 2); 112 | $(this).remove(); 113 | } 114 | }); 115 | $('td.memItemLeft').each(function(){ 116 | if($(this).siblings('.memItemRight').html()=="") { 117 | $(this).attr('colspan', 2); 118 | $(this).siblings('.memItemRight').remove(); 119 | } 120 | }); 121 | }); 122 | -------------------------------------------------------------------------------- /docs/footer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /docs/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | $projectname: $title 15 | $title 16 | 17 | 18 | $treeview 19 | $search 20 | $mathjax 21 | 22 | $extrastylesheet 23 | 24 | 25 | 26 | 27 | 28 | 29 | 36 |
37 |
38 |
39 |
40 |
41 |
42 | 43 | -------------------------------------------------------------------------------- /docs/style.css: -------------------------------------------------------------------------------- 1 | /* The standard CSS for doxygen 1.8.6 */ 2 | 3 | body, table, div, p, dl { 4 | font: 400 14px/22px Roboto, RobotDraft, Ubuntu Arial, sans-serif; 5 | } 6 | 7 | /* @group Heading Levels */ 8 | 9 | h1.groupheader { 10 | font-size: 150%; 11 | } 12 | 13 | .title { 14 | font: 400 14px/28px Roboto,sans-serif; 15 | font-size: 150%; 16 | font-weight: bold; 17 | margin: 10px 2px; 18 | } 19 | 20 | h2.groupheader { 21 | border-bottom: 1px solid #879ECB; 22 | color: #354C7B; 23 | font-size: 150%; 24 | font-weight: normal; 25 | margin-top: 1.75em; 26 | padding-top: 8px; 27 | padding-bottom: 4px; 28 | width: 100%; 29 | } 30 | 31 | h3.groupheader { 32 | font-size: 100%; 33 | } 34 | 35 | h1, h2, h3, h4, h5, h6 { 36 | -webkit-transition: text-shadow 0.5s linear; 37 | -moz-transition: text-shadow 0.5s linear; 38 | -ms-transition: text-shadow 0.5s linear; 39 | -o-transition: text-shadow 0.5s linear; 40 | transition: text-shadow 0.5s linear; 41 | margin-right: 15px; 42 | } 43 | 44 | h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow { 45 | text-shadow: 0 0 15px cyan; 46 | } 47 | 48 | dt { 49 | font-weight: bold; 50 | } 51 | 52 | div.multicol { 53 | -moz-column-gap: 1em; 54 | -webkit-column-gap: 1em; 55 | -moz-column-count: 3; 56 | -webkit-column-count: 3; 57 | } 58 | 59 | p.startli, p.startdd { 60 | margin-top: 2px; 61 | } 62 | 63 | p.starttd { 64 | margin-top: 0px; 65 | } 66 | 67 | p.endli { 68 | margin-bottom: 0px; 69 | } 70 | 71 | p.enddd { 72 | margin-bottom: 4px; 73 | } 74 | 75 | p.endtd { 76 | margin-bottom: 2px; 77 | } 78 | 79 | /* @end */ 80 | 81 | caption { 82 | font-weight: bold; 83 | } 84 | 85 | span.legend { 86 | font-size: 70%; 87 | text-align: center; 88 | } 89 | 90 | h3.version { 91 | font-size: 90%; 92 | text-align: center; 93 | } 94 | 95 | div.qindex, div.navtab{ 96 | background-color: #EBEFF6; 97 | border: 1px solid #A3B4D7; 98 | text-align: center; 99 | } 100 | 101 | div.qindex, div.navpath { 102 | width: 100%; 103 | line-height: 140%; 104 | } 105 | 106 | div.navtab { 107 | margin-right: 15px; 108 | } 109 | 110 | /* @group Link Styling */ 111 | 112 | a { 113 | color: #3D578C; 114 | font-weight: normal; 115 | text-decoration: none; 116 | } 117 | 118 | .contents a:visited { 119 | color: #4665A2; 120 | } 121 | 122 | a:hover { 123 | text-decoration: underline; 124 | } 125 | 126 | a.qindex { 127 | font-weight: bold; 128 | } 129 | 130 | a.qindexHL { 131 | font-weight: bold; 132 | background-color: #9CAFD4; 133 | color: #ffffff; 134 | border: 1px double #869DCA; 135 | } 136 | 137 | .contents a.qindexHL:visited { 138 | color: #ffffff; 139 | } 140 | 141 | a.el { 142 | font-weight: bold; 143 | } 144 | 145 | a.elRef { 146 | } 147 | 148 | a.code, a.code:visited, a.line, a.line:visited { 149 | color: #4665A2; 150 | } 151 | 152 | a.codeRef, a.codeRef:visited, a.lineRef, a.lineRef:visited { 153 | color: #4665A2; 154 | } 155 | 156 | /* @end */ 157 | 158 | dl.el { 159 | margin-left: -1cm; 160 | } 161 | 162 | pre.fragment { 163 | border: 1px solid #C4CFE5; 164 | background-color: #FBFCFD; 165 | padding: 4px 6px; 166 | margin: 4px 8px 4px 2px; 167 | overflow: auto; 168 | word-wrap: break-word; 169 | font-size: 9pt; 170 | line-height: 125%; 171 | font-family: monospace, fixed; 172 | font-size: 105%; 173 | } 174 | 175 | div.fragment { 176 | padding: 4px 6px; 177 | margin: 4px 8px 4px 2px; 178 | background-color: #FBFCFD; 179 | border: 1px solid #C4CFE5; 180 | } 181 | 182 | div.line { 183 | font-family: monospace, fixed; 184 | font-size: 13px; 185 | min-height: 13px; 186 | line-height: 1.0; 187 | text-wrap: unrestricted; 188 | white-space: -moz-pre-wrap; /* Moz */ 189 | white-space: -pre-wrap; /* Opera 4-6 */ 190 | white-space: -o-pre-wrap; /* Opera 7 */ 191 | white-space: pre-wrap; /* CSS3 */ 192 | word-wrap: break-word; /* IE 5.5+ */ 193 | text-indent: -53px; 194 | padding-left: 53px; 195 | padding-bottom: 0px; 196 | margin: 0px; 197 | -webkit-transition-property: background-color, box-shadow; 198 | -webkit-transition-duration: 0.5s; 199 | -moz-transition-property: background-color, box-shadow; 200 | -moz-transition-duration: 0.5s; 201 | -ms-transition-property: background-color, box-shadow; 202 | -ms-transition-duration: 0.5s; 203 | -o-transition-property: background-color, box-shadow; 204 | -o-transition-duration: 0.5s; 205 | transition-property: background-color, box-shadow; 206 | transition-duration: 0.5s; 207 | } 208 | 209 | div.line.glow { 210 | background-color: cyan; 211 | box-shadow: 0 0 10px cyan; 212 | } 213 | 214 | 215 | span.lineno { 216 | padding-right: 4px; 217 | text-align: right; 218 | border-right: 2px solid #0F0; 219 | background-color: #E8E8E8; 220 | white-space: pre; 221 | } 222 | span.lineno a { 223 | background-color: #D8D8D8; 224 | } 225 | 226 | span.lineno a:hover { 227 | background-color: #C8C8C8; 228 | } 229 | 230 | div.ah { 231 | background-color: black; 232 | font-weight: bold; 233 | color: #ffffff; 234 | margin-bottom: 3px; 235 | margin-top: 3px; 236 | padding: 0.2em; 237 | border: solid thin #333; 238 | border-radius: 0.5em; 239 | -webkit-border-radius: .5em; 240 | -moz-border-radius: .5em; 241 | box-shadow: 2px 2px 3px #999; 242 | background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444)); 243 | background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000); 244 | } 245 | 246 | .tabs, .tabs1, .tabs2, .tabs3 { 247 | background-image: none; 248 | background-color: #ddd; 249 | } 250 | 251 | .tablist a, 252 | .tablist a:hover, 253 | .tablist li { 254 | background-image: none; 255 | border-right: 1px solid gray; 256 | } 257 | 258 | div.groupHeader { 259 | margin-left: 16px; 260 | margin-top: 12px; 261 | font-weight: bold; 262 | } 263 | 264 | div.groupText { 265 | margin-left: 16px; 266 | font-style: italic; 267 | } 268 | 269 | body { 270 | background-color: white; 271 | color: black; 272 | margin: 0; 273 | } 274 | 275 | div.contents { 276 | margin-top: 10px; 277 | margin-left: 12px; 278 | margin-right: 8px; 279 | } 280 | 281 | td.indexkey { 282 | background-color: #EBEFF6; 283 | font-weight: bold; 284 | border: 1px solid #C4CFE5; 285 | margin: 2px 0px 2px 0; 286 | padding: 2px 10px; 287 | white-space: nowrap; 288 | vertical-align: top; 289 | } 290 | 291 | td.indexvalue { 292 | background-color: #EBEFF6; 293 | border: 1px solid #C4CFE5; 294 | padding: 2px 10px; 295 | margin: 2px 0px; 296 | } 297 | 298 | tr.memlist { 299 | background-color: #EEF1F7; 300 | } 301 | 302 | p.formulaDsp { 303 | text-align: center; 304 | } 305 | 306 | img.formulaDsp { 307 | 308 | } 309 | 310 | img.formulaInl { 311 | vertical-align: middle; 312 | } 313 | 314 | div.center { 315 | text-align: center; 316 | margin-top: 0px; 317 | margin-bottom: 0px; 318 | padding: 0px; 319 | } 320 | 321 | div.center img { 322 | border: 0px; 323 | } 324 | 325 | address.footer { 326 | text-align: right; 327 | padding-right: 12px; 328 | } 329 | 330 | img.footer { 331 | border: 0px; 332 | vertical-align: middle; 333 | } 334 | 335 | /* @group Code Colorization */ 336 | 337 | span.keyword { 338 | color: #008000 339 | } 340 | 341 | span.keywordtype { 342 | color: #604020 343 | } 344 | 345 | span.keywordflow { 346 | color: #e08000 347 | } 348 | 349 | span.comment { 350 | color: #800000 351 | } 352 | 353 | span.preprocessor { 354 | color: #806020 355 | } 356 | 357 | span.stringliteral { 358 | color: #002080 359 | } 360 | 361 | span.charliteral { 362 | color: #008080 363 | } 364 | 365 | span.vhdldigit { 366 | color: #ff00ff 367 | } 368 | 369 | span.vhdlchar { 370 | color: #000000 371 | } 372 | 373 | span.vhdlkeyword { 374 | color: #700070 375 | } 376 | 377 | span.vhdllogic { 378 | color: #ff0000 379 | } 380 | 381 | blockquote { 382 | background-color: #F7F8FB; 383 | border-left: 2px solid #9CAFD4; 384 | margin: 0 24px 0 4px; 385 | padding: 0 12px 0 16px; 386 | } 387 | 388 | /* @end */ 389 | 390 | /* 391 | .search { 392 | color: #003399; 393 | font-weight: bold; 394 | } 395 | 396 | form.search { 397 | margin-bottom: 0px; 398 | margin-top: 0px; 399 | } 400 | 401 | input.search { 402 | font-size: 75%; 403 | color: #000080; 404 | font-weight: normal; 405 | background-color: #e8eef2; 406 | } 407 | */ 408 | 409 | td.tiny { 410 | font-size: 75%; 411 | } 412 | 413 | .dirtab { 414 | padding: 4px; 415 | border-collapse: collapse; 416 | border: 1px solid #A3B4D7; 417 | } 418 | 419 | th.dirtab { 420 | background: #EBEFF6; 421 | font-weight: bold; 422 | } 423 | 424 | hr { 425 | height: 0px; 426 | border: none; 427 | border-top: 1px solid #4A6AAA; 428 | } 429 | 430 | hr.footer { 431 | height: 1px; 432 | } 433 | 434 | /* @group Member Descriptions */ 435 | 436 | table.memberdecls { 437 | border-spacing: 0px; 438 | padding: 0px; 439 | } 440 | 441 | .memberdecls td, .fieldtable tr { 442 | -webkit-transition-property: background-color, box-shadow; 443 | -webkit-transition-duration: 0.5s; 444 | -moz-transition-property: background-color, box-shadow; 445 | -moz-transition-duration: 0.5s; 446 | -ms-transition-property: background-color, box-shadow; 447 | -ms-transition-duration: 0.5s; 448 | -o-transition-property: background-color, box-shadow; 449 | -o-transition-duration: 0.5s; 450 | transition-property: background-color, box-shadow; 451 | transition-duration: 0.5s; 452 | } 453 | 454 | .memberdecls td.glow, .fieldtable tr.glow { 455 | background-color: cyan; 456 | box-shadow: 0 0 15px cyan; 457 | } 458 | 459 | .mdescLeft, .mdescRight, 460 | .memItemLeft, .memItemRight, 461 | .memTemplItemLeft, .memTemplItemRight, .memTemplParams { 462 | background-color: #F9FAFC; 463 | border: none; 464 | margin: 4px; 465 | padding: 1px 0 0 8px; 466 | } 467 | 468 | .mdescLeft, .mdescRight { 469 | padding: 0px 8px 4px 8px; 470 | color: #555; 471 | } 472 | 473 | .memSeparator { 474 | border-bottom: 1px solid #DEE4F0; 475 | line-height: 1px; 476 | margin: 0px; 477 | padding: 0px; 478 | } 479 | 480 | .memItemLeft, .memTemplItemLeft { 481 | white-space: nowrap; 482 | } 483 | 484 | .memItemRight { 485 | width: 100%; 486 | } 487 | 488 | .memTemplParams { 489 | color: #4665A2; 490 | white-space: nowrap; 491 | font-size: 80%; 492 | } 493 | 494 | /* @end */ 495 | 496 | /* @group Member Details */ 497 | 498 | /* Styles for detailed member documentation */ 499 | 500 | .memtemplate { 501 | font-size: 80%; 502 | color: #4665A2; 503 | font-weight: normal; 504 | margin-left: 9px; 505 | } 506 | 507 | .memnav { 508 | background-color: #EBEFF6; 509 | border: 1px solid #A3B4D7; 510 | text-align: center; 511 | margin: 2px; 512 | margin-right: 15px; 513 | padding: 2px; 514 | } 515 | 516 | .mempage { 517 | width: 100%; 518 | } 519 | 520 | .memitem { 521 | padding: 0; 522 | margin-bottom: 15px; 523 | margin-right: 5px; 524 | -webkit-transition: box-shadow 0.5s linear; 525 | -moz-transition: box-shadow 0.5s linear; 526 | -ms-transition: box-shadow 0.5s linear; 527 | -o-transition: box-shadow 0.5s linear; 528 | transition: box-shadow 0.5s linear; 529 | display: table !important; 530 | width: 100%; 531 | box-shadow: 0px 2px 15px black; 532 | } 533 | 534 | .memitem.glow { 535 | box-shadow: 0 0 15px cyan; 536 | } 537 | 538 | .memname { 539 | font-weight: bold; 540 | margin-left: 6px; 541 | } 542 | 543 | .memname td { 544 | vertical-align: bottom; 545 | } 546 | 547 | .memproto, dl.reflist dt { 548 | padding: 6px 0px 6px 0px; 549 | color: #253555; 550 | font-weight: bold; 551 | text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); 552 | background-color: #E2E8F2; 553 | 554 | } 555 | 556 | .memdoc, dl.reflist dd { 557 | padding: 6px 10px 2px 10px; 558 | background-color: #FBFCFD; 559 | } 560 | 561 | dl.reflist dt { 562 | padding: 5px; 563 | } 564 | 565 | dl.reflist dd { 566 | margin: 0px 0px 10px 0px; 567 | padding: 5px; 568 | } 569 | 570 | .paramkey { 571 | text-align: right; 572 | } 573 | 574 | .paramtype { 575 | white-space: nowrap; 576 | } 577 | 578 | .paramname { 579 | color: #602020; 580 | white-space: nowrap; 581 | } 582 | .paramname em { 583 | font-style: normal; 584 | } 585 | .paramname code { 586 | line-height: 14px; 587 | } 588 | 589 | .params, .retval, .exception, .tparams { 590 | margin-left: 0px; 591 | padding-left: 0px; 592 | } 593 | 594 | .params .paramname, .retval .paramname { 595 | font-weight: bold; 596 | vertical-align: top; 597 | } 598 | 599 | .params .paramtype { 600 | font-style: italic; 601 | vertical-align: top; 602 | } 603 | 604 | .params .paramdir { 605 | font-family: "courier new",courier,monospace; 606 | vertical-align: top; 607 | } 608 | 609 | table.mlabels { 610 | border-spacing: 0px; 611 | } 612 | 613 | td.mlabels-left { 614 | width: 100%; 615 | padding: 0px; 616 | } 617 | 618 | td.mlabels-right { 619 | vertical-align: bottom; 620 | padding: 0px; 621 | white-space: nowrap; 622 | } 623 | 624 | span.mlabels { 625 | margin-left: 8px; 626 | } 627 | 628 | span.mlabel { 629 | background-color: #728DC1; 630 | border-top:1px solid #5373B4; 631 | border-left:1px solid #5373B4; 632 | border-right:1px solid #C4CFE5; 633 | border-bottom:1px solid #C4CFE5; 634 | text-shadow: none; 635 | color: white; 636 | margin-right: 4px; 637 | padding: 2px 3px; 638 | border-radius: 3px; 639 | font-size: 7pt; 640 | white-space: nowrap; 641 | vertical-align: middle; 642 | } 643 | 644 | 645 | 646 | /* @end */ 647 | 648 | /* these are for tree view when not used as main index */ 649 | 650 | div.directory { 651 | margin: 10px 0px; 652 | border-top: 1px solid #A8B8D9; 653 | border-bottom: 1px solid #A8B8D9; 654 | width: 100%; 655 | } 656 | 657 | .directory table { 658 | border-collapse:collapse; 659 | } 660 | 661 | .directory td { 662 | margin: 0px; 663 | padding: 0px; 664 | vertical-align: top; 665 | } 666 | 667 | .directory td.entry { 668 | white-space: nowrap; 669 | padding-right: 6px; 670 | padding-top: 3px; 671 | } 672 | 673 | .directory td.entry a { 674 | outline:none; 675 | } 676 | 677 | .directory td.entry a img { 678 | border: none; 679 | } 680 | 681 | .directory td.desc { 682 | width: 100%; 683 | padding-left: 6px; 684 | padding-right: 6px; 685 | padding-top: 3px; 686 | border-left: 1px solid rgba(0,0,0,0.05); 687 | } 688 | 689 | .directory tr.even { 690 | padding-left: 6px; 691 | background-color: #F7F8FB; 692 | } 693 | 694 | .directory img { 695 | vertical-align: -30%; 696 | } 697 | 698 | .directory .levels { 699 | white-space: nowrap; 700 | width: 100%; 701 | text-align: right; 702 | font-size: 9pt; 703 | } 704 | 705 | .directory .levels span { 706 | cursor: pointer; 707 | padding-left: 2px; 708 | padding-right: 2px; 709 | color: #3D578C; 710 | } 711 | 712 | div.dynheader { 713 | margin-top: 8px; 714 | -webkit-touch-callout: none; 715 | -webkit-user-select: none; 716 | -khtml-user-select: none; 717 | -moz-user-select: none; 718 | -ms-user-select: none; 719 | user-select: none; 720 | } 721 | 722 | address { 723 | font-style: normal; 724 | color: #2A3D61; 725 | } 726 | 727 | table.doxtable { 728 | border-collapse:collapse; 729 | margin-top: 4px; 730 | margin-bottom: 4px; 731 | } 732 | 733 | table.doxtable td, table.doxtable th { 734 | border: 1px solid #2D4068; 735 | padding: 3px 7px 2px; 736 | } 737 | 738 | table.doxtable th { 739 | background-color: #374F7F; 740 | color: #FFFFFF; 741 | font-size: 110%; 742 | padding-bottom: 4px; 743 | padding-top: 5px; 744 | } 745 | 746 | table.fieldtable { 747 | /*width: 100%;*/ 748 | margin-bottom: 10px; 749 | border: 1px solid #A8B8D9; 750 | border-spacing: 0px; 751 | -moz-border-radius: 4px; 752 | -webkit-border-radius: 4px; 753 | border-radius: 4px; 754 | box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); 755 | } 756 | 757 | .fieldtable td, .fieldtable th { 758 | padding: 3px 7px 2px; 759 | } 760 | 761 | .fieldtable td.fieldtype, .fieldtable td.fieldname { 762 | white-space: nowrap; 763 | border-right: 1px solid #A8B8D9; 764 | border-bottom: 1px solid #A8B8D9; 765 | vertical-align: top; 766 | } 767 | 768 | .fieldtable td.fieldname { 769 | padding-top: 3px; 770 | } 771 | 772 | .fieldtable td.fielddoc { 773 | border-bottom: 1px solid #A8B8D9; 774 | /*width: 100%;*/ 775 | } 776 | 777 | .fieldtable td.fielddoc p:first-child { 778 | margin-top: 0px; 779 | } 780 | 781 | .fieldtable td.fielddoc p:last-child { 782 | margin-bottom: 2px; 783 | } 784 | 785 | .fieldtable tr:last-child td { 786 | border-bottom: none; 787 | } 788 | 789 | .fieldtable th { 790 | background-image:url('nav_f.png'); 791 | background-repeat:repeat-x; 792 | background-color: #E2E8F2; 793 | font-size: 90%; 794 | color: #253555; 795 | padding-bottom: 4px; 796 | padding-top: 5px; 797 | text-align:left; 798 | -moz-border-radius-topleft: 4px; 799 | -moz-border-radius-topright: 4px; 800 | -webkit-border-top-left-radius: 4px; 801 | -webkit-border-top-right-radius: 4px; 802 | border-top-left-radius: 4px; 803 | border-top-right-radius: 4px; 804 | border-bottom: 1px solid #A8B8D9; 805 | } 806 | 807 | 808 | .tabsearch { 809 | top: 0px; 810 | left: 10px; 811 | height: 36px; 812 | background-image: url('tab_b.png'); 813 | z-index: 101; 814 | overflow: hidden; 815 | font-size: 13px; 816 | } 817 | 818 | .navpath ul 819 | { 820 | font-size: 11px; 821 | background-image:url('tab_b.png'); 822 | background-repeat:repeat-x; 823 | background-position: 0 -5px; 824 | height:30px; 825 | line-height:30px; 826 | color:#8AA0CC; 827 | border:solid 1px #C2CDE4; 828 | overflow:hidden; 829 | margin:0px; 830 | padding:0px; 831 | } 832 | 833 | .navpath li 834 | { 835 | list-style-type:none; 836 | float:left; 837 | padding-left:10px; 838 | padding-right:15px; 839 | background-image:url('bc_s.png'); 840 | background-repeat:no-repeat; 841 | background-position:right; 842 | color:#364D7C; 843 | } 844 | 845 | .navpath li.navelem a 846 | { 847 | height:32px; 848 | display:block; 849 | text-decoration: none; 850 | outline: none; 851 | color: #283A5D; 852 | font-family: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; 853 | text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); 854 | text-decoration: none; 855 | } 856 | 857 | .navpath li.navelem a:hover 858 | { 859 | color:#6884BD; 860 | } 861 | 862 | .navpath li.footer 863 | { 864 | list-style-type:none; 865 | float:right; 866 | padding-left:10px; 867 | padding-right:15px; 868 | background-image:none; 869 | background-repeat:no-repeat; 870 | background-position:right; 871 | color:#364D7C; 872 | font-size: 8pt; 873 | } 874 | 875 | 876 | div.summary 877 | { 878 | float: right; 879 | font-size: 8pt; 880 | padding-right: 5px; 881 | width: 50%; 882 | text-align: right; 883 | } 884 | 885 | div.summary a 886 | { 887 | white-space: nowrap; 888 | } 889 | 890 | div.ingroups 891 | { 892 | font-size: 8pt; 893 | width: 50%; 894 | text-align: left; 895 | } 896 | 897 | div.ingroups a 898 | { 899 | white-space: nowrap; 900 | } 901 | 902 | div.header 903 | { 904 | background-image:url('nav_h.png'); 905 | background-repeat:repeat-x; 906 | background-color: #F9FAFC; 907 | margin: 0px; 908 | border-bottom: 1px solid #C4CFE5; 909 | } 910 | 911 | div.headertitle 912 | { 913 | padding: 5px 5px 5px 10px; 914 | } 915 | 916 | dl 917 | { 918 | padding: 0 0 0 10px; 919 | } 920 | 921 | /* dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug */ 922 | dl.section 923 | { 924 | margin-left: 0px; 925 | padding-left: 0px; 926 | } 927 | 928 | dl.note 929 | { 930 | margin-left:-7px; 931 | padding-left: 3px; 932 | border-left:4px solid; 933 | border-color: #D0C000; 934 | } 935 | 936 | dl.warning, dl.attention 937 | { 938 | margin-left:-7px; 939 | padding-left: 3px; 940 | border-left:4px solid; 941 | border-color: #FF0000; 942 | } 943 | 944 | dl.pre, dl.post, dl.invariant 945 | { 946 | margin-left:-7px; 947 | padding-left: 3px; 948 | border-left:4px solid; 949 | border-color: #00D000; 950 | } 951 | 952 | dl.deprecated 953 | { 954 | margin-left:-7px; 955 | padding-left: 3px; 956 | border-left:4px solid; 957 | border-color: #505050; 958 | } 959 | 960 | dl.todo 961 | { 962 | margin-left:-7px; 963 | padding-left: 3px; 964 | border-left:4px solid; 965 | border-color: #00C0E0; 966 | } 967 | 968 | dl.test 969 | { 970 | margin-left:-7px; 971 | padding-left: 3px; 972 | border-left:4px solid; 973 | border-color: #3030E0; 974 | } 975 | 976 | dl.bug 977 | { 978 | margin-left:-7px; 979 | padding-left: 3px; 980 | border-left:4px solid; 981 | border-color: #C08050; 982 | } 983 | 984 | dl.section dd { 985 | margin-bottom: 6px; 986 | } 987 | 988 | 989 | #projectlogo 990 | { 991 | text-align: center; 992 | vertical-align: bottom; 993 | border-collapse: separate; 994 | } 995 | 996 | #projectlogo img 997 | { 998 | border: 0px none; 999 | } 1000 | 1001 | #projectname 1002 | { 1003 | font: 300% Tahoma, Arial,sans-serif; 1004 | margin: 0px; 1005 | padding: 2px 0px; 1006 | } 1007 | 1008 | #projectbrief 1009 | { 1010 | font: 120% Tahoma, Arial,sans-serif; 1011 | margin: 0px; 1012 | padding: 0px; 1013 | } 1014 | 1015 | #projectnumber 1016 | { 1017 | font: 50% Tahoma, Arial,sans-serif; 1018 | margin: 0px; 1019 | padding: 0px; 1020 | } 1021 | 1022 | #titlearea 1023 | { 1024 | padding: 0px; 1025 | margin: 0px; 1026 | width: 100%; 1027 | border-bottom: 1px solid #5373B4; 1028 | } 1029 | 1030 | .image 1031 | { 1032 | text-align: center; 1033 | } 1034 | 1035 | .dotgraph 1036 | { 1037 | text-align: center; 1038 | } 1039 | 1040 | .mscgraph 1041 | { 1042 | text-align: center; 1043 | } 1044 | 1045 | .diagraph 1046 | { 1047 | text-align: center; 1048 | } 1049 | 1050 | .caption 1051 | { 1052 | font-weight: bold; 1053 | } 1054 | 1055 | div.zoom 1056 | { 1057 | border: 1px solid #90A5CE; 1058 | } 1059 | 1060 | dl.citelist { 1061 | margin-bottom:50px; 1062 | } 1063 | 1064 | dl.citelist dt { 1065 | color:#334975; 1066 | float:left; 1067 | font-weight:bold; 1068 | margin-right:10px; 1069 | padding:5px; 1070 | } 1071 | 1072 | dl.citelist dd { 1073 | margin:2px 0; 1074 | padding:5px 0; 1075 | } 1076 | 1077 | div.toc { 1078 | padding: 14px 25px; 1079 | background-color: #F4F6FA; 1080 | border: 1px solid #D8DFEE; 1081 | border-radius: 7px 7px 7px 7px; 1082 | float: right; 1083 | height: auto; 1084 | margin: 0 20px 10px 10px; 1085 | width: 200px; 1086 | } 1087 | 1088 | div.toc li { 1089 | background: url("bdwn.png") no-repeat scroll 0 5px transparent; 1090 | font: 10px/1.2 Verdana,DejaVu Sans,Geneva,sans-serif; 1091 | margin-top: 5px; 1092 | padding-left: 10px; 1093 | padding-top: 2px; 1094 | } 1095 | 1096 | div.toc h3 { 1097 | font: bold 12px/1.2 Arial,FreeSans,sans-serif; 1098 | color: #4665A2; 1099 | border-bottom: 0 none; 1100 | margin: 0; 1101 | } 1102 | 1103 | div.toc ul { 1104 | list-style: none outside none; 1105 | border: medium none; 1106 | padding: 0px; 1107 | } 1108 | 1109 | div.toc li.level1 { 1110 | margin-left: 0px; 1111 | } 1112 | 1113 | div.toc li.level2 { 1114 | margin-left: 15px; 1115 | } 1116 | 1117 | div.toc li.level3 { 1118 | margin-left: 30px; 1119 | } 1120 | 1121 | div.toc li.level4 { 1122 | margin-left: 45px; 1123 | } 1124 | 1125 | .inherit_header { 1126 | font-weight: bold; 1127 | color: gray; 1128 | cursor: pointer; 1129 | -webkit-touch-callout: none; 1130 | -webkit-user-select: none; 1131 | -khtml-user-select: none; 1132 | -moz-user-select: none; 1133 | -ms-user-select: none; 1134 | user-select: none; 1135 | } 1136 | 1137 | .inherit_header td { 1138 | padding: 6px 0px 2px 5px; 1139 | } 1140 | 1141 | .inherit { 1142 | display: none; 1143 | } 1144 | 1145 | tr.heading h2 { 1146 | margin-top: 12px; 1147 | margin-bottom: 4px; 1148 | } 1149 | 1150 | /* tooltip related style info */ 1151 | 1152 | .ttc { 1153 | position: absolute; 1154 | display: none; 1155 | } 1156 | 1157 | #powerTip { 1158 | cursor: default; 1159 | white-space: nowrap; 1160 | background-color: white; 1161 | border: 1px solid gray; 1162 | border-radius: 4px 4px 4px 4px; 1163 | box-shadow: 1px 1px 7px gray; 1164 | display: none; 1165 | font-size: smaller; 1166 | max-width: 80%; 1167 | opacity: 0.9; 1168 | padding: 1ex 1em 1em; 1169 | position: absolute; 1170 | z-index: 2147483647; 1171 | } 1172 | 1173 | #powerTip div.ttdoc { 1174 | color: grey; 1175 | font-style: italic; 1176 | } 1177 | 1178 | #powerTip div.ttname a { 1179 | font-weight: bold; 1180 | } 1181 | 1182 | #powerTip div.ttname { 1183 | font-weight: bold; 1184 | } 1185 | 1186 | #powerTip div.ttdeci { 1187 | color: #006318; 1188 | } 1189 | 1190 | #powerTip div { 1191 | margin: 0px; 1192 | padding: 0px; 1193 | font: 12px/16px Roboto,sans-serif; 1194 | } 1195 | 1196 | #powerTip:before, #powerTip:after { 1197 | content: ""; 1198 | position: absolute; 1199 | margin: 0px; 1200 | } 1201 | 1202 | #powerTip.n:after, #powerTip.n:before, 1203 | #powerTip.s:after, #powerTip.s:before, 1204 | #powerTip.w:after, #powerTip.w:before, 1205 | #powerTip.e:after, #powerTip.e:before, 1206 | #powerTip.ne:after, #powerTip.ne:before, 1207 | #powerTip.se:after, #powerTip.se:before, 1208 | #powerTip.nw:after, #powerTip.nw:before, 1209 | #powerTip.sw:after, #powerTip.sw:before { 1210 | border: solid transparent; 1211 | content: " "; 1212 | height: 0; 1213 | width: 0; 1214 | position: absolute; 1215 | } 1216 | 1217 | #powerTip.n:after, #powerTip.s:after, 1218 | #powerTip.w:after, #powerTip.e:after, 1219 | #powerTip.nw:after, #powerTip.ne:after, 1220 | #powerTip.sw:after, #powerTip.se:after { 1221 | border-color: rgba(255, 255, 255, 0); 1222 | } 1223 | 1224 | #powerTip.n:before, #powerTip.s:before, 1225 | #powerTip.w:before, #powerTip.e:before, 1226 | #powerTip.nw:before, #powerTip.ne:before, 1227 | #powerTip.sw:before, #powerTip.se:before { 1228 | border-color: rgba(128, 128, 128, 0); 1229 | } 1230 | 1231 | #powerTip.n:after, #powerTip.n:before, 1232 | #powerTip.ne:after, #powerTip.ne:before, 1233 | #powerTip.nw:after, #powerTip.nw:before { 1234 | top: 100%; 1235 | } 1236 | 1237 | #powerTip.n:after, #powerTip.ne:after, #powerTip.nw:after { 1238 | border-top-color: #ffffff; 1239 | border-width: 10px; 1240 | margin: 0px -10px; 1241 | } 1242 | #powerTip.n:before { 1243 | border-top-color: #808080; 1244 | border-width: 11px; 1245 | margin: 0px -11px; 1246 | } 1247 | #powerTip.n:after, #powerTip.n:before { 1248 | left: 50%; 1249 | } 1250 | 1251 | #powerTip.nw:after, #powerTip.nw:before { 1252 | right: 14px; 1253 | } 1254 | 1255 | #powerTip.ne:after, #powerTip.ne:before { 1256 | left: 14px; 1257 | } 1258 | 1259 | #powerTip.s:after, #powerTip.s:before, 1260 | #powerTip.se:after, #powerTip.se:before, 1261 | #powerTip.sw:after, #powerTip.sw:before { 1262 | bottom: 100%; 1263 | } 1264 | 1265 | #powerTip.s:after, #powerTip.se:after, #powerTip.sw:after { 1266 | border-bottom-color: #ffffff; 1267 | border-width: 10px; 1268 | margin: 0px -10px; 1269 | } 1270 | 1271 | #powerTip.s:before, #powerTip.se:before, #powerTip.sw:before { 1272 | border-bottom-color: #808080; 1273 | border-width: 11px; 1274 | margin: 0px -11px; 1275 | } 1276 | 1277 | #powerTip.s:after, #powerTip.s:before { 1278 | left: 50%; 1279 | } 1280 | 1281 | #powerTip.sw:after, #powerTip.sw:before { 1282 | right: 14px; 1283 | } 1284 | 1285 | #powerTip.se:after, #powerTip.se:before { 1286 | left: 14px; 1287 | } 1288 | 1289 | #powerTip.e:after, #powerTip.e:before { 1290 | left: 100%; 1291 | } 1292 | #powerTip.e:after { 1293 | border-left-color: #ffffff; 1294 | border-width: 10px; 1295 | top: 50%; 1296 | margin-top: -10px; 1297 | } 1298 | #powerTip.e:before { 1299 | border-left-color: #808080; 1300 | border-width: 11px; 1301 | top: 50%; 1302 | margin-top: -11px; 1303 | } 1304 | 1305 | #powerTip.w:after, #powerTip.w:before { 1306 | right: 100%; 1307 | } 1308 | #powerTip.w:after { 1309 | border-right-color: #ffffff; 1310 | border-width: 10px; 1311 | top: 50%; 1312 | margin-top: -10px; 1313 | } 1314 | #powerTip.w:before { 1315 | border-right-color: #808080; 1316 | border-width: 11px; 1317 | top: 50%; 1318 | margin-top: -11px; 1319 | } 1320 | 1321 | @media print 1322 | { 1323 | #top { display: none; } 1324 | #side-nav { display: none; } 1325 | #nav-path { display: none; } 1326 | body { overflow:visible; } 1327 | h1, h2, h3, h4, h5, h6 { page-break-after: avoid; } 1328 | .summary { display: none; } 1329 | .memitem { page-break-inside: avoid; } 1330 | #doc-content 1331 | { 1332 | margin-left:0 !important; 1333 | height:auto !important; 1334 | width:auto !important; 1335 | overflow:inherit; 1336 | display:inline; 1337 | } 1338 | } 1339 | 1340 | -------------------------------------------------------------------------------- /source/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(CMakeParseArguments) 2 | 3 | set(PEW_HEADER_DIRECTORIES adio) 4 | add_library(adio 5 | adio/config.hpp 6 | adio/docs.hpp 7 | adio/utils.hpp 8 | adio/traits.hpp 9 | adio/error.hpp 10 | adio/error.cpp 11 | adio/connection_fwd.hpp 12 | adio/connection.hpp 13 | adio/connection.cpp 14 | adio/service.hpp 15 | adio/sql/value.hpp 16 | adio/sql/value.cpp 17 | adio/sql/row.hpp 18 | ) 19 | 20 | target_link_libraries(adio PUBLIC boost::variant) 21 | 22 | if(ASIO_BACKEND STREQUAL boost) 23 | target_compile_definitions(adio PUBLIC ADIO_DETAIL_USE_BOOST_ASIO_DEFAULT) 24 | target_link_libraries(adio PUBLIC $) 25 | elseif(ASIO_BACKEND STREQUAL vanilla) 26 | target_compile_definitions(adio PUBLIC ADIO_DETAIL_USE_VANILLA_ASIO_DEFAULT) 27 | target_link_libraries(adio PUBLIC $) 28 | else() 29 | message(SEND_ERROR "Unknown ASIO_BACKEND: ${ASIO_BACKEND}") 30 | endif() 31 | 32 | target_compile_features(adio PUBLIC cxx_alias_templates cxx_auto_type cxx_variadic_templates cxx_lambdas) 33 | set_property(TARGET adio PROPERTY CXX_EXTENSIONS FALSE) 34 | 35 | target_include_directories(adio PUBLIC $) 36 | 37 | if(NOT MSVC) 38 | target_link_libraries(adio PUBLIC -pthread) 39 | endif() 40 | 41 | add_library(adio::adio ALIAS adio) 42 | 43 | if(NOT Pew_FOUND) 44 | set_property(TARGET adio PROPERTY EXPORT_NAME adio::adio) 45 | install(TARGETS adio 46 | EXPORT adioTargets 47 | RUNTIME DESTINATION bin 48 | ARCHIVE DESTINATION lib 49 | LIBRARY DESTINATION lib 50 | INCLUDES DESTINATION include 51 | ) 52 | install(DIRECTORY adio 53 | DESTINATION include 54 | FILES_MATCHING 55 | PATTERN *.hpp 56 | ) 57 | endif() 58 | 59 | function(adio_backend name) 60 | cmake_parse_arguments( 61 | ARGS 62 | "" 63 | "" 64 | "SOURCES;LINK_LIBRARIES;INCLUDE_DIRECTORIES" 65 | ${ARGN}) 66 | set(PEW_HEADER_DIRECTORIES adio) 67 | add_library(adio-${name} ${ARGS_SOURCES}) 68 | target_link_libraries(adio-${name} PUBLIC adio::adio ${ARGS_LINK_LIBRARIES}) 69 | target_include_directories(adio-${name} PUBLIC ${ARGS_INCLUDE_DIRECTORIES}) 70 | set_property(TARGET adio-${name} PROPERTY EXPORT_NAME adio::${driver}) 71 | add_library(adio::${name} ALIAS adio-${name}) 72 | target_include_directories(adio-${name} PUBLIC $) 73 | if(NOT Pew_FOUND) 74 | set_property(TARGET adio-${name} PROPERTY EXPORT_NAME adio::${name}) 75 | install(TARGETS adio-${name} 76 | EXPORT adioTargets 77 | RUNTIME DESTINATION bin 78 | ARCHIVE DESTINATION lib 79 | LIBRARY DESTINATION lib 80 | INCLUDES DESTINATION include 81 | ) 82 | install(DIRECTORY adio 83 | DESTINATION include 84 | FILES_MATCHING 85 | PATTERN *.hpp 86 | ) 87 | endif() 88 | if(NOT MSVC) 89 | target_compile_options(adio-${name} PRIVATE -Wall -Wextra) 90 | endif() 91 | endfunction() 92 | 93 | add_subdirectory(drivers) 94 | 95 | -------------------------------------------------------------------------------- /source/adio/config.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ADIO_CONFIG_HPP_INCLUDED 2 | #define ADIO_CONFIG_HPP_INCLUDED 3 | 4 | #include 5 | 6 | // clang-format off 7 | // Adio is built with either using boost or vanilla by default, use can 8 | // override: 9 | #ifdef ADIO_USE_BOOST_ASIO 10 | // Use requested Boost.Asio 11 | # include 12 | # define ADIO_DETAIL_BOOST_ASIO 13 | #elif defined(ADIO_USE_VANILLA_ASIO) 14 | // Use requests vanilla Asio 15 | # include 16 | # include 17 | # define ADIO_DETAIL_VANILLA_ASIO 18 | #else 19 | // Use has not overriden the default: 20 | # ifdef ADIO_DETAIL_USE_BOOST_ASIO_DEFAULT 21 | // Built against boost::asio: 22 | # include 23 | # define ASIO_DETAIL_BOOST_ASIO 24 | # elif defined(ADIO_DETAIL_USE_VANILLA_ASIO_DEFAULT) 25 | // Built against vanilla Asio: 26 | # include 27 | # include 28 | # define ASIO_DETAIL_VANILLA_ASIO 29 | # else 30 | // Unable to determine which asio to use! 31 | # error "Define either ADIO_USE_BOOST_ASIO or ADIO_USE_VANILLA_ASIO" 32 | # endif 33 | #endif 34 | // clang-format on 35 | 36 | #ifdef ASIO_DETAIL_VANILLA_ASIO 37 | // Vanilla asio uses the stdlib: 38 | #include 39 | #endif 40 | 41 | namespace adio 42 | { 43 | 44 | #ifdef ASIO_DETAIL_VANILLA_ASIO 45 | 46 | namespace asio = asio; 47 | 48 | using error_code = ADIO_DOC_UNSPEC(std::error_code); 49 | using error_condition = ADIO_DOC_UNSPEC(std::error_condition); 50 | using sys_errc = ADIO_DOC_UNSPEC(std::errc); 51 | using system_error = ADIO_DOC_UNSPEC(std::system_error); 52 | using asio_error_category = ADIO_DOC_UNSPEC(std::error_category); 53 | using std::make_error_code; 54 | using std::make_error_condition; 55 | 56 | #else // Use Boost.Asio: 57 | 58 | namespace asio = ADIO_DOC_UNSPEC(boost::asio); 59 | /// Either ``std::error_code`` or ``boost::system::error_code`` 60 | using error_code = ADIO_DOC_UNSPEC(boost::system::error_code); 61 | /// Either ``std::error_condition`` or ``boost::system::error_condition`` 62 | using error_condition = ADIO_DOC_UNSPEC(boost::system::error_condition); 63 | /// Either ``std::errc`` or ``boost::system::errc::errc_t`` 64 | using sys_errc = ADIO_DOC_UNSPEC(boost::system::errc::errc_t); 65 | /// Either ``std::system_error`` or ``boost::system::system_error`` 66 | using system_error = ADIO_DOC_UNSPEC(boost::system::system_error); 67 | /// Either ``std::error_category`` or ``boost::system::error_category`` 68 | using asio_error_category = ADIO_DOC_UNSPEC(boost::system::error_category); 69 | using boost::system::errc::make_error_code; 70 | using boost::system::errc::make_error_condition; 71 | 72 | #endif 73 | 74 | using io_service = asio::io_service; 75 | 76 | using std::string; 77 | 78 | } /* adio */ 79 | 80 | #ifdef ADIO_DETAIL_VANILLA_ASIO 81 | #define ADIO_DECLARE_ERRC_ENUM(type) \ 82 | namespace std \ 83 | { \ 84 | template <> struct is_error_code_enum \ 85 | { \ 86 | enum \ 87 | { \ 88 | value = true \ 89 | }; \ 90 | }; \ 91 | template <> struct is_error_condition_enum \ 92 | { \ 93 | enum \ 94 | { \ 95 | value = true \ 96 | }; \ 97 | }; \ 98 | } /* std */ \ 99 | static_assert(true, "") 100 | #else 101 | #define ADIO_DECLARE_ERRC_ENUM(type) \ 102 | namespace boost \ 103 | { \ 104 | namespace system \ 105 | { \ 106 | \ 107 | template <> struct is_error_code_enum \ 108 | { \ 109 | enum \ 110 | { \ 111 | value = true \ 112 | }; \ 113 | }; \ 114 | } /* system */ \ 115 | } /* boost */ \ 116 | static_assert(true, "") 117 | #endif // ADIO_DETAIL_VANILLA_ASIO 118 | 119 | #endif // ADIO_CONFIG_HPP_INCLUDED 120 | -------------------------------------------------------------------------------- /source/adio/connection.cpp: -------------------------------------------------------------------------------- 1 | #include "connection.hpp" 2 | 3 | 4 | -------------------------------------------------------------------------------- /source/adio/connection.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ADIO_DATABASE_CONNECTION_HPP_INCLUDED 2 | #define ADIO_DATABASE_CONNECTION_HPP_INCLUDED 3 | 4 | #include "config.hpp" 5 | 6 | #include "connection_fwd.hpp" 7 | 8 | namespace adio 9 | { 10 | 11 | /** Represents a database connection, through which a user may execute queries. 12 | * 13 | * @param Driver The backend of the database connection, such as SQLite, 14 | * PostgreSQL, etc. 15 | * 16 | * This class is an Asio IO object, so it should be used in conjuction with 17 | * ``asio::io_service``, similar to using an Asio socket object. 18 | * 19 | * This class exposes four primary operations: ``open``, ``close``, ``prepare``, 20 | * and ``execute``. There are four synchronous methods and four asynchronous 21 | * methods corresponding to these operations. The return type and argument types 22 | * for each is not specified within the ``basic_connection`` class template, but 23 | * is defined by the backend ``Driver`` type. To see the precise semantics of 24 | * these methods, refer to the driver classes themselves. For example, see 25 | * ``adio::sqlite``. 26 | */ 27 | template 28 | class basic_connection : public asio::basic_io_object 29 | { 30 | public: 31 | /// Our own type 32 | using self_type = basic_connection; 33 | /// The type of statement used by the backend 34 | using statement = ADIO_DOC_IMPLDEF(typename Driver::statement); 35 | using service = ADIO_DOC_IMPLDEF(typename Driver::service); 36 | /// @internal 37 | using super_type = asio::basic_io_object; 38 | 39 | /** Constructs a new connection. 40 | * 41 | * @param ios An Asio ``io_service`` instance. 42 | */ 43 | explicit basic_connection(asio::io_service& ios) 44 | : super_type{ios} 45 | { 46 | } 47 | 48 | #define ADIO_CON_DECL_FN(name) \ 49 | template \ 50 | ADIO_DOC_IMPLDEF(auto) \ 51 | name(Args&&... args) ADIO_DOC_UNSPEC( \ 52 | ->decltype(this->get_service().name(this->get_implementation(), \ 53 | std::forward(args)...))) \ 54 | { \ 55 | return this->get_service().name(this->get_implementation(), \ 56 | std::forward(args)...); \ 57 | } \ 58 | template \ 59 | ADIO_DOC_IMPLDEF(auto) \ 60 | async_##name(Args&&... args) \ 61 | ADIO_DOC_UNSPEC(->decltype( \ 62 | this->get_service() \ 63 | .async_##name(this->get_implementation(), \ 64 | std::forward(args)...))) \ 65 | { \ 66 | return this->get_service().async_##name(this->get_implementation(), \ 67 | std::forward(args)...); \ 68 | } \ 69 | static_assert(true, "") 70 | 71 | ADIO_CON_DECL_FN(open); 72 | ADIO_CON_DECL_FN(prepare); 73 | ADIO_CON_DECL_FN(execute); 74 | ADIO_CON_DECL_FN(step); 75 | ADIO_CON_DECL_FN(close); 76 | #undef ADIO_CON_DECL_FN 77 | }; 78 | 79 | } /* adio */ 80 | 81 | #endif // ADIO_DATABASE_CONNECTION_HPP_INCLUDED 82 | -------------------------------------------------------------------------------- /source/adio/connection_fwd.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ADIO_CONNECTION_FWD_HPP_INCLUDED 2 | #define ADIO_CONNECTION_FWD_HPP_INCLUDED 3 | 4 | namespace adio 5 | { 6 | 7 | 8 | template 9 | class basic_connection; 10 | 11 | } /* adio */ 12 | 13 | #endif // ADIO_CONNECTION_FWD_HPP_INCLUDED 14 | -------------------------------------------------------------------------------- /source/adio/docs.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ADIO_DOCS_HPP_INCLUDED 2 | #define ADIO_DOCS_HPP_INCLUDED 3 | 4 | // Macros used for documentation purposes only 5 | #define ADIO_DOC_IMPLDEF(...) __VA_ARGS__ 6 | #define ADIO_DOC_IMPLDEF2(...) __VA_ARGS__ 7 | #define ADIO_DOC_IMPLDEF3(...) __VA_ARGS__ 8 | #define ADIO_DOC_IMPLDEF4(...) __VA_ARGS__ 9 | #define ADIO_DOC_UNSPEC(...) __VA_ARGS__ 10 | #define ADIO_DOC_UNSPEC2(...) __VA_ARGS__ 11 | #define ADIO_DOC_UNSPEC3(...) __VA_ARGS__ 12 | #define ADIO_DOC_UNSPEC4(...) __VA_ARGS__ 13 | 14 | #define ADIO_IGNORE(...) 15 | #define ADIO_JUST(...) __VA_ARGS__ 16 | 17 | #ifndef DOXYGEN_GENERATING_DOCUMENTATION 18 | #define ADIO_DOCS_LIE(documented, ...) ADIO_JUST 19 | #endif 20 | 21 | /** 22 | * @mainpage Hello! 23 | * 24 | * This is the main page 25 | */ 26 | 27 | /** 28 | * @page custom_adaptors Adapting types to adio::value and adio::row 29 | * 30 | * In addition to database handling, Adio provides facilities for helping to 31 | * serialize custom datatypes to and from the basic ``adio::value`` and 32 | * ``adio::row`` classes. The simplistic types representable in databases may be 33 | * entirely insufficient to encode the invariants that we wish to implement in 34 | * custom classes and data structures. Ensuring constraints via the database at 35 | * runtime is costly compared to simply letting the C++ type system handle it at 36 | * compile-time. 37 | * 38 | * Say we have a type named ``Widget`` that we wish to use as a column in a 39 | * database. Let us also assume that ``Widget`` can be serialized into a 40 | * lossless textual representation using some ``to_string()`` function, and 41 | * recreated using ``Widget::from_string()``. This means that we can use a 42 | * SQL ``TEXT`` field to store ``Widget`` objects. 43 | * 44 | * We can tell ``adio::value`` how to work with ``Widget`` by specializing the 45 | * ``adio::value_adaptor`` class template. 46 | * 47 | * ~~~cpp 48 | * namespace adio 49 | * { 50 | * 51 | * template<> struct value_adaptor 52 | * { 53 | * using base_type = std::string; 54 | * enum { nullable = false }; 55 | * static Widget convert(const base_type& str) 56 | * { 57 | * return Widget::from_string(str); 58 | * } 59 | * static base_type convert(const Widget& widget) 60 | * { 61 | * return to_string(Widget); 62 | * } 63 | * }; 64 | * 65 | * } 66 | * ~~~ 67 | * 68 | * And that's it! Some explaination may be required: 69 | * 70 | * - ``base_type`` must be a typedef to the type which we serialize *to* and 71 | * deserialize *from*. It must be either one of the basic types supported by 72 | * ``adio::value``, or a type which also has a valid specialization of 73 | * ``value_adaptor``. 74 | * 75 | * - ``nullable`` must be a constant expression, either ``true`` or ``false``. 76 | * We'll talk about this a bit more below. 77 | * 78 | * - Two overloads of ``convert``: One must accept an instance of ``base_type`` 79 | * and return our custom type; this is the *deserialization* function. The other 80 | * should accept an instance of our custom type and return a new instance of 81 | * ``base_type``; this is the *serialization* function. 82 | * 83 | * If we have a custom type that has a "NULL" state, or something akin to it, 84 | * (ie. ``boost::optional``), we use the ``nullable`` member to tell Adio 85 | * about it. Adaptors with ``nullable`` set to ``true`` have a few more 86 | * requirements. For example, here is a value adaptor for ``boost::optional``: 87 | * 88 | * ~~~cpp 89 | * namespace adio 90 | * { 91 | * 92 | * template struct value_adaptor> 93 | * { 94 | * using base_type = Data; 95 | * enum { nullable = true }; // Set this to true 96 | * static base_type convert(const boost::optional& opt) 97 | * { 98 | * return *opt; // Note: No null check here 99 | * } 100 | * static boost::optional convert(const base_type& base) 101 | * { 102 | * return boost::optional{base}; 103 | * } 104 | * static bool is_null(const boost::optional& opt) 105 | * { 106 | * return bool{ opt }; 107 | * } 108 | * static boost::optional null() { return boost::none; } 109 | * { 110 | * return boost::none; 111 | * } 112 | * }; 113 | * 114 | * } 115 | * ~~~ 116 | * 117 | * There are two more static member functions required for a ``nullable`` 118 | * adaptor: 119 | * 120 | * - ``is_null`` accepts an instance of our adapted type and should return 121 | * ``true`` *iff* the object represents the NULL state. In this case, 122 | * ``boost::optional`` is "null" if it is not engaged. 123 | * 124 | * - ``null`` accepts no arguments and should return an instance of our adapted 125 | * type that represents the NULL state. For ``boost::optional``, we simple 126 | * construct an optional from ``boost::none``. 127 | * 128 | * It is guaranteed that Adio will never invoke ``convert(T) -> base_type`` with 129 | * a "null" instance of T. Thus in the example above, we need not check that the 130 | * ``optional`` instance is a null option, and we simply retrieve the value from 131 | * inside. 132 | */ 133 | 134 | #endif // ADIO_DOCS_HPP_INCLUDED 135 | -------------------------------------------------------------------------------- /source/adio/error.cpp: -------------------------------------------------------------------------------- 1 | #include "error.hpp" 2 | 3 | 4 | using namespace adio; 5 | 6 | namespace adio 7 | { 8 | 9 | namespace detail 10 | { 11 | 12 | class error_category : public adio::asio_error_category 13 | { 14 | const char* name() const noexcept override; 15 | std::string message(int) const override; 16 | }; 17 | 18 | } /* detail */ 19 | 20 | } /* adio */ 21 | 22 | 23 | const char* detail::error_category::name() const noexcept 24 | { 25 | return "adio::base"; 26 | } 27 | 28 | std::string detail::error_category::message(int) const 29 | { 30 | return "Unkown error"; 31 | } 32 | 33 | const asio_error_category& adio::error_category() 34 | { 35 | static detail::error_category cat; 36 | return cat; 37 | } 38 | -------------------------------------------------------------------------------- /source/adio/error.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ADIO_ERROR_HPP_INCLUDED 2 | #define ADIO_ERROR_HPP_INCLUDED 3 | 4 | #include "config.hpp" 5 | 6 | namespace adio 7 | { 8 | 9 | enum class errc 10 | { 11 | }; 12 | 13 | extern const asio_error_category& error_category(); 14 | 15 | } /* adio */ 16 | 17 | #endif // ADIO_ERROR_HPP_INCLUDED 18 | -------------------------------------------------------------------------------- /source/adio/service.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ADIO_DB_SERVICE_BASE_HPP_INCLUDED 2 | #define ADIO_DB_SERVICE_BASE_HPP_INCLUDED 3 | 4 | #include "config.hpp" 5 | #include "traits.hpp" 6 | #include "utils.hpp" 7 | 8 | #include 9 | 10 | namespace adio 11 | { 12 | 13 | namespace detail 14 | { 15 | 16 | template 17 | class db_service_base : public asio::io_service::service 18 | { 19 | public: 20 | static asio::io_service::id id; 21 | 22 | using self_type = db_service_base; 23 | using super_type = asio::io_service::service; 24 | 25 | using implementation_type = std::shared_ptr; 26 | 27 | db_service_base(asio::io_service& ios) 28 | : super_type(ios) 29 | { 30 | } 31 | 32 | void construct(implementation_type& impl) 33 | { 34 | impl.reset(new Driver(static_cast(*this))); 35 | } 36 | void destroy(implementation_type& impl) { impl.reset(); } 37 | 38 | #define ADIO_SERVICE_DECL_FN(name, ...) \ 39 | public: \ 40 | template \ 41 | auto name(implementation_type& impl, Args&&... args) \ 42 | ->decltype(impl->name(std::forward(args)...)) \ 43 | { \ 44 | return impl->name(std::forward(args)...); \ 45 | } \ 46 | \ 47 | private: \ 48 | template struct invoker_for_##name \ 49 | { \ 50 | template \ 51 | void \ 52 | _invoke(implementation_type& impl, Args&&... args, Handler&& handler) \ 53 | { \ 54 | static_assert( \ 55 | adio::handler_matches::value, \ 58 | "Invalid async handler passed to async_" #name \ 59 | " for this database driver"); \ 60 | impl->async_##name(std::forward(args)..., handler); \ 61 | } \ 62 | template \ 63 | auto operator()(implementation_type& impl, \ 64 | Args&&... args, \ 65 | Handler&& handler) \ 66 | -> decltype(std::declval>&>() \ 69 | .result.get()) \ 70 | { \ 71 | handler_helper> \ 73 | init{std::forward(handler)}; \ 74 | _invoke(impl, std::forward(args)..., init.handler); \ 75 | return init.result.get(); \ 76 | } \ 77 | }; \ 78 | template \ 79 | auto _async_##name(detail::tag, \ 80 | implementation_type& impl, \ 81 | Args&&... args) \ 82 | ->decltype( \ 83 | invoker_for_##name{}(impl, \ 84 | std::forward(args)...)) \ 85 | { \ 86 | return invoker_for_##name{}(impl, \ 87 | std::forward(args)...); \ 88 | } \ 89 | \ 90 | public: \ 91 | template \ 92 | auto async_##name(implementation_type& impl, Args&&... args) \ 93 | ->decltype(this->_async_##name(detail::pop_back{}, \ 94 | impl, \ 95 | std::forward(args)...)) \ 96 | { \ 97 | /* Defer to the invoke helpers. This helps us automatically get the \ 98 | * handler from the tail of the argument list */ \ 99 | return _async_##name(detail::pop_back{}, \ 100 | impl, \ 101 | std::forward(args)...); \ 102 | } \ 103 | static_assert(true, "") 104 | 105 | ADIO_SERVICE_DECL_FN(open); 106 | ADIO_SERVICE_DECL_FN(prepare); 107 | ADIO_SERVICE_DECL_FN(execute); 108 | ADIO_SERVICE_DECL_FN(step); 109 | ADIO_SERVICE_DECL_FN(close); 110 | 111 | private: 112 | void shutdown_service() override{}; 113 | }; 114 | 115 | template 116 | io_service::id db_service_base::id; 117 | 118 | } /* detail */ 119 | 120 | } /* adio */ 121 | 122 | #endif // ADIO_DB_SERVICE_BASE_HPP_INCLUDED 123 | -------------------------------------------------------------------------------- /source/adio/sql/row.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ADIO_ROW_HPP_INCLUDED 2 | #define ADIO_ROW_HPP_INCLUDED 3 | 4 | #include "value.hpp" 5 | 6 | #include 7 | 8 | namespace adio 9 | { 10 | 11 | template struct row_adaptor; 12 | 13 | class row 14 | { 15 | std::vector _data; 16 | 17 | public: 18 | explicit row(std::vector d) 19 | : _data{std::move(d)} 20 | { 21 | } 22 | 23 | std::size_t size() const { return _data.size(); } 24 | 25 | const value& operator[](std::size_t n) const { return _data[n]; } 26 | 27 | template T as() const { return row_adaptor::read(*this); } 28 | 29 | template explicit operator T() const { return as(); } 30 | }; 31 | 32 | template const value& get(const row& r) { return r[N]; } 33 | 34 | 35 | namespace detail 36 | { 37 | 38 | inline void check_singular(const row& r) 39 | { 40 | if (r.size() != 1) 41 | throw std::invalid_argument{ 42 | "Cannot read single value from row of width != 1"}; 43 | } 44 | 45 | template 46 | struct int_row_adaptor 47 | { 48 | static T read(const row& r) 49 | { 50 | detail::check_singular(r); 51 | return T(r[0].get()); 52 | } 53 | }; 54 | 55 | } /* detail */ 56 | 57 | #define DECL_INT_ADAPTOR(t) \ 58 | template <> struct row_adaptor : detail::int_row_adaptor \ 59 | { \ 60 | } 61 | DECL_INT_ADAPTOR(std::uint8_t); 62 | DECL_INT_ADAPTOR(std::int8_t); 63 | DECL_INT_ADAPTOR(std::uint16_t); 64 | DECL_INT_ADAPTOR(std::int16_t); 65 | DECL_INT_ADAPTOR(std::uint32_t); 66 | DECL_INT_ADAPTOR(std::int32_t); 67 | DECL_INT_ADAPTOR(std::uint64_t); 68 | DECL_INT_ADAPTOR(std::int64_t); 69 | #undef DECL_INT_ADAPTOR 70 | 71 | template 72 | struct row_adaptor> 73 | { 74 | using value_type = std::basic_string; 75 | static value_type read(const row& r) 76 | { 77 | detail::check_singular(r); 78 | auto str = r[0].get(); 79 | return value_type(begin(str), end(str)); 80 | } 81 | }; 82 | 83 | } /* adio */ 84 | 85 | #endif // ADIO_ROW_HPP_INCLUDED 86 | 87 | -------------------------------------------------------------------------------- /source/adio/sql/value.cpp: -------------------------------------------------------------------------------- 1 | #include "value.hpp" 2 | 3 | adio::null_t adio::null; 4 | -------------------------------------------------------------------------------- /source/adio/sql/value.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ADIO_VALUE_HPP_INCLUDED 2 | #define ADIO_VALUE_HPP_INCLUDED 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | namespace adio 15 | { 16 | 17 | using boost::variant; 18 | 19 | /// Represents the type of some database value 20 | enum class type 21 | { 22 | null_t, 23 | integer, 24 | real, 25 | text, 26 | blob, 27 | datetime, 28 | }; 29 | 30 | /// Special class used to represent NULL database values. @see adio::null 31 | class null_t final : public boost::operators { 32 | bool operator==(null_t) const { return true; } 33 | bool operator<(null_t) const { return false; } 34 | }; 35 | /// An instance of ``null_t`` 36 | extern null_t null; 37 | 38 | /// Exception throw when accessing a ``value`` object using the incorrect type. 39 | class invalid_access : public std::runtime_error 40 | { 41 | public: 42 | invalid_access(std::string s) 43 | : std::runtime_error{s} 44 | { 45 | } 46 | }; 47 | 48 | using boost::get; 49 | using std::get; 50 | 51 | /// Type used to adapt to and from ``value`` (See @ref custom_adaptors "Custom adaptors") 52 | template struct value_adaptor; 53 | template struct is_basic_type; 54 | 55 | template 56 | struct has_value_adaptor : std::false_type 57 | { 58 | }; 59 | 60 | template 61 | struct has_value_adaptor::base_type>> 62 | : std::true_type 63 | { 64 | }; 65 | 66 | class value : boost::operators 67 | { 68 | public: 69 | using null_t = adio::null_t; 70 | using integer = int64_t; 71 | using real = double; 72 | using text = std::string; 73 | using blob = std::vector; 74 | using datetime = std::chrono::high_resolution_clock::time_point; 75 | using variant_type = variant; 76 | 77 | private: 78 | variant_type _data; 79 | 80 | #define DECL_GETTER(tn) \ 81 | tn _get(detail::tag) const \ 82 | { \ 83 | return get_type() == type::tn \ 84 | ? boost::get(_data) \ 85 | : throw invalid_access{"Cannot get a " #tn \ 86 | " value from a non-" #tn \ 87 | " value object (Type is " \ 88 | + std::string{type_name()} \ 89 | + ")"}; \ 90 | } 91 | 92 | DECL_GETTER(null_t); 93 | DECL_GETTER(integer); 94 | DECL_GETTER(real); 95 | DECL_GETTER(text); 96 | DECL_GETTER(blob); 97 | DECL_GETTER(datetime); 98 | 99 | template T _get_maybe_nullable(std::true_type) const 100 | { 101 | using adaptor = value_adaptor; 102 | if (get_type() == type::null_t) return adaptor::null(); 103 | auto value = get(); 104 | return adaptor::convert(value); 105 | } 106 | 107 | template T _get_maybe_nullable(std::false_type) const 108 | { 109 | using adaptor = value_adaptor; 110 | auto value = get(); 111 | return adaptor::convert(value); 112 | } 113 | 114 | template ::type>> 116 | static value _from_maybe_nullable(T&& other, std::true_type) 117 | { 118 | return Adaptor::is_null(other) 119 | ? value{null} 120 | : value{Adaptor::convert(std::forward(other))}; 121 | } 122 | 123 | template ::type>> 125 | static value _from_maybe_nullable(T&& other, std::false_type) 126 | { 127 | return value{Adaptor::convert(std::forward(other))}; 128 | } 129 | 130 | public: 131 | value(null_t = null) 132 | : _data{null} 133 | { 134 | } 135 | value(integer i) 136 | : _data{i} 137 | { 138 | } 139 | value(real r) 140 | : _data{r} 141 | { 142 | } 143 | template ::type, 146 | typename 147 | = typename std::enable_if::value 148 | && !is_basic_type::value>::type, 149 | typename Adaptor = value_adaptor)> 150 | value(Other&& other) 151 | : value( 152 | _from_maybe_nullable(std::forward(other), 153 | std::integral_constant{})) 155 | { 156 | } 157 | value(text t) 158 | : _data{std::move(t)} 159 | { 160 | } 161 | value(blob b) 162 | : _data{std::move(b)} 163 | { 164 | } 165 | value(datetime d) 166 | : _data{d} 167 | { 168 | } 169 | template 170 | value(const char (&arr)[N]) 171 | : _data{text(arr)} 172 | { 173 | } 174 | 175 | template 176 | typename std::enable_if::value, T>::type get() const 177 | { 178 | return _get(detail::tag{}); 179 | } 180 | 181 | template 182 | typename std::enable_if::value, T>::type get() const 183 | { 184 | using adaptor = value_adaptor; 185 | static_assert(has_value_adaptor::value, 186 | "Cannot convert an adio::value object to the given type: " 187 | "No adaptor has been provided"); 188 | return _get_maybe_nullable( 189 | std::integral_constant{}); 190 | } 191 | 192 | enum type get_type() const 193 | { 194 | switch (_data.which()) 195 | { 196 | case 0: 197 | return type::null_t; 198 | case 1: 199 | return type::integer; 200 | case 2: 201 | return type::real; 202 | case 3: 203 | return type::text; 204 | case 4: 205 | return type::blob; 206 | case 5: 207 | return type::datetime; 208 | default: 209 | assert(0); 210 | std::terminate(); 211 | } 212 | } 213 | 214 | const char* type_name() const 215 | { 216 | switch (get_type()) 217 | { 218 | case type::null_t: 219 | return "NULL"; 220 | case type::integer: 221 | return "integral"; 222 | case type::real: 223 | return "real"; 224 | case type::text: 225 | return "text"; 226 | case type::blob: 227 | return "blob"; 228 | case type::datetime: 229 | return "datetime"; 230 | default: 231 | assert(0); 232 | std::terminate(); 233 | } 234 | } 235 | 236 | template explicit operator T() const { return get(); } 237 | 238 | inline bool operator<(const value& other) const; 239 | inline bool operator==(const value& other) const; 240 | }; 241 | 242 | template struct is_basic_type : std::false_type 243 | { 244 | }; 245 | template <> struct is_basic_type : std::true_type 246 | { 247 | }; 248 | template <> struct is_basic_type : std::true_type 249 | { 250 | }; 251 | template <> struct is_basic_type : std::true_type 252 | { 253 | }; 254 | template <> struct is_basic_type : std::true_type 255 | { 256 | }; 257 | template <> struct is_basic_type : std::true_type 258 | { 259 | }; 260 | template <> struct is_basic_type : std::true_type 261 | { 262 | }; 263 | 264 | template T get(const value& val) { return val.get(); } 265 | 266 | inline bool value::operator<(const value& other) const 267 | { 268 | const auto other_t = other.get_type(); 269 | if (get_type() != other_t) return get_type() < other_t; 270 | switch (other_t) 271 | { 272 | case type::null_t: 273 | return false; 274 | case type::integer: 275 | return get() < other.get(); 276 | case type::real: 277 | return get() < other.get(); 278 | case type::text: 279 | return get() < other.get(); 280 | case type::blob: 281 | return get() < other.get(); 282 | case type::datetime: 283 | return get() < other.get(); 284 | default: 285 | assert(0); 286 | std::terminate(); 287 | } 288 | } 289 | 290 | inline bool value::operator==(const value& other) const 291 | { 292 | const auto other_t = other.get_type(); 293 | if (get_type() != other_t) return false; 294 | switch (other_t) 295 | { 296 | case type::null_t: 297 | return true; 298 | case type::integer: 299 | return get() == other.get(); 300 | case type::real: 301 | return get() == other.get(); 302 | case type::text: 303 | return get() == other.get(); 304 | case type::blob: 305 | return get() == other.get(); 306 | case type::datetime: 307 | return get() == other.get(); 308 | default: 309 | assert(0); 310 | std::terminate(); 311 | } 312 | } 313 | 314 | inline std::ostream& operator<<(std::ostream& o, const value& v) 315 | { 316 | switch (v.get_type()) 317 | { 318 | case type::null_t: 319 | return o << "NULL"; 320 | case type::integer: 321 | return o << get(v); 322 | case type::real: 323 | return o << get(v); 324 | case type::text: 325 | return o << get(v); 326 | case type::blob: 327 | return o << "{" << get(v).size() << " bytes of data}"; 328 | case type::datetime: 329 | return o << get(v); 330 | default: 331 | assert(0); 332 | std::terminate(); 333 | } 334 | } 335 | 336 | namespace detail 337 | { 338 | 339 | template struct integer_adaptor 340 | { 341 | enum 342 | { 343 | nullable = false 344 | }; 345 | using base_type = value::integer; 346 | static Int convert(base_type v) { return static_cast(v); } 347 | static base_type convert(Int v) { return static_cast(v); } 348 | }; 349 | 350 | } /* detail */ 351 | 352 | #define DECL_INT_ADAPTOR(t) \ 353 | template <> struct value_adaptor : detail::integer_adaptor \ 354 | { \ 355 | } 356 | DECL_INT_ADAPTOR(std::int8_t); 357 | DECL_INT_ADAPTOR(std::uint8_t); 358 | DECL_INT_ADAPTOR(std::int16_t); 359 | DECL_INT_ADAPTOR(std::uint16_t); 360 | DECL_INT_ADAPTOR(std::int32_t); 361 | DECL_INT_ADAPTOR(std::uint32_t); 362 | DECL_INT_ADAPTOR(std::uint64_t); 363 | DECL_INT_ADAPTOR(bool); 364 | #undef DECL_INT_ADAPTOR 365 | 366 | template 367 | struct value_adaptor> 368 | { 369 | enum 370 | { 371 | nullable = false 372 | }; 373 | using base_type = std::string; 374 | using value_type = std::basic_string; 375 | static value_type convert(const base_type& str) 376 | { 377 | return value_type{std::begin(str), std::end(str)}; 378 | } 379 | static base_type convert(const value_type& str) 380 | { 381 | return base_type{begin(str), end(str)}; 382 | } 383 | }; 384 | 385 | } /* adio */ 386 | 387 | #endif // ADIO_VALUE_HPP_INCLUDED 388 | -------------------------------------------------------------------------------- /source/adio/traits.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ADIO_TRAITS_HPP_INCLUDED 2 | #define ADIO_TRAITS_HPP_INCLUDED 3 | 4 | #include 5 | 6 | #include 7 | 8 | namespace adio 9 | { 10 | 11 | namespace detail 12 | { 13 | 14 | template struct void_t_helper 15 | { 16 | using type = void; 17 | }; 18 | 19 | } /* detail */ 20 | 21 | template using void_t = typename detail::void_t_helper::type; 22 | 23 | #define ADIO_DECLARE_MEMBER_DETECTOR(name, member) \ 24 | template struct name : public std::false_type \ 25 | { \ 26 | }; \ 27 | template \ 28 | struct name> : public std::true_type \ 29 | { \ 30 | } 31 | 32 | ADIO_DECLARE_MEMBER_DETECTOR(has_implementation_type, implementation_type); 33 | 34 | #define ADIO_MEMBER_TYPE_OR(name, member) \ 35 | namespace detail \ 36 | { \ 37 | template \ 38 | struct name##_helper \ 39 | { \ 40 | using type = Fallback; \ 41 | }; \ 42 | template \ 43 | struct name##_helper> \ 44 | { \ 45 | using type = typename T::member; \ 46 | }; \ 47 | } /* detail */ \ 48 | template \ 49 | using name = typename detail::name##_helper::type 50 | 51 | ADIO_MEMBER_TYPE_OR(implementation_type_or, implementation_type); 52 | 53 | template 54 | using handler_decay = typename std::remove_reference< 55 | typename std::remove_cv::type>::type; 56 | 57 | template struct handler_helper 58 | { 59 | using RealHandlerType = 60 | typename asio::handler_type, Signature>::type; 61 | 62 | RealHandlerType handler; 63 | asio::async_result result; 64 | 65 | handler_helper(const Handler& h) 66 | : handler(h) 67 | , result(handler) 68 | { 69 | } 70 | }; 71 | 72 | namespace detail 73 | { 74 | 75 | // This speedy impelementation brought to you by Xeo, 76 | // https://stackoverflow.com/questions/13072359/c11-compile-time-array-with-logarithmic-evaluation-depth/13073076#13073076 77 | template using Invoke = typename T::type; 78 | 79 | template struct seq 80 | { 81 | using type = seq; 82 | }; 83 | 84 | template struct concat; 85 | 86 | template 87 | struct concat, seq> : seq 88 | { 89 | }; 90 | 91 | template using Concat = Invoke>; 92 | 93 | template struct gen_seq; 94 | template using GenSeq = Invoke>; 95 | 96 | template 97 | struct gen_seq : Concat, GenSeq> 98 | { 99 | }; 100 | 101 | template <> struct gen_seq<0> : seq<> 102 | { 103 | }; 104 | template <> struct gen_seq<1> : seq<0> 105 | { 106 | }; 107 | 108 | template struct tag 109 | { 110 | }; 111 | 112 | template struct type_index_pair 113 | { 114 | friend T my_declval(type_index_pair, std::integral_constant) 115 | { 116 | } 117 | }; 118 | 119 | template struct pop_back_helper; 120 | 121 | template 122 | struct pop_back_helper, seq> 123 | { 124 | struct base : type_index_pair... 125 | { 126 | }; 127 | 128 | template 129 | using join = tag{}))...>; 131 | }; 132 | 133 | template 134 | auto deduce(seq, seq) -> 135 | typename pop_back_helper, seq>::template join; 136 | 137 | template 138 | using pop_back = decltype( 139 | deduce(gen_seq{}, gen_seq{})); 140 | 141 | 142 | } /* detail */ 143 | 144 | template 145 | struct handler_matches : std::false_type 146 | { 147 | }; 148 | 149 | template 150 | struct handler_matches()( 153 | std::declval()...))>> 154 | : std::integral_constant()( 156 | std::declval()...)), 157 | Ret>::value> 158 | { 159 | }; 160 | 161 | template 162 | using require_handler_matches = 163 | typename std::enable_if::value, Ret>::type; 164 | 165 | } /* adio */ 166 | 167 | #endif // ADIO_TRAITS_HPP_INCLUDED 168 | -------------------------------------------------------------------------------- /source/adio/utils.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ADIO_UTILS_HPP_INCLUDED 2 | #define ADIO_UTILS_HPP_INCLUDED 3 | 4 | #include "config.hpp" 5 | 6 | #include 7 | #include 8 | 9 | namespace adio 10 | { 11 | 12 | namespace detail 13 | { 14 | 15 | template 16 | std::unique_ptr make_unique(Args&&... args) 17 | { 18 | return std::unique_ptr{new T(std::forward(args)...)}; 19 | } 20 | 21 | inline std::shared_ptr make_work(io_service& ios) 22 | { 23 | return std::make_shared(ios); 24 | } 25 | 26 | inline void throw_if_error(const error_code& e, const string& what) 27 | { 28 | if (e) throw system_error{e, what}; 29 | } 30 | 31 | using std::begin; 32 | using std::end; 33 | 34 | } /* detail */ 35 | 36 | using std::begin; 37 | using std::end; 38 | 39 | } /* adio */ 40 | 41 | #endif // ADIO_UTILS_HPP_INCLUDED 42 | -------------------------------------------------------------------------------- /source/drivers/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(sqlite) 2 | add_subdirectory(postgresql) 3 | add_subdirectory(empty) 4 | -------------------------------------------------------------------------------- /source/drivers/empty/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | adio_backend(empty 2 | SOURCES 3 | adio/empty.hpp 4 | adio/empty.cpp 5 | ) 6 | -------------------------------------------------------------------------------- /source/drivers/empty/adio/empty.cpp: -------------------------------------------------------------------------------- 1 | #include "empty.hpp" 2 | -------------------------------------------------------------------------------- /source/drivers/empty/adio/empty.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ADIO_EMPTY_DRIVER_HPP_INCLUDED 2 | #define ADIO_EMPTY_DRIVER_HPP_INCLUDED 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | namespace adio 10 | { 11 | 12 | class empty_service; 13 | 14 | class empty_driver 15 | { 16 | std::reference_wrapper _parent_ios; 17 | template void _post(Handler&& h) 18 | { 19 | _parent_ios.get().post(std::forward(h)); 20 | } 21 | 22 | public: 23 | using self_type = empty_driver; 24 | 25 | using statement = std::nullptr_t; 26 | using service = empty_service; 27 | using connection = basic_connection; 28 | 29 | empty_driver(empty_service& ios); 30 | 31 | using execute_handler_signature = void(); 32 | using open_handler_signature = void(); 33 | using step_handler_signatuve = void(); 34 | 35 | void open(const std::string& path) 36 | { 37 | std::cerr << "Opened fake database: " << path << '\n'; 38 | } 39 | 40 | template 41 | void async_open(const std::string& path, Handler&& h) 42 | { 43 | std::cerr << "Async opened fake database: " << path << '\n'; 44 | _post(std::forward(h)); 45 | } 46 | 47 | void execute(const std::string& str) 48 | { 49 | std::cerr << "Executing query: " << str << '\n'; 50 | } 51 | 52 | template 53 | void async_execute(const std::string& str, Handler&& h) 54 | { 55 | std::cerr << "Executing async query: " << str << '\n'; 56 | _post(std::forward(h)); 57 | } 58 | 59 | int step(std::string) { return 12; } 60 | 61 | int prepare(const std::string&) const { return 42; } 62 | void close() {} 63 | }; 64 | 65 | class empty_service 66 | : public detail::db_service_base 67 | { 68 | public: 69 | using db_service_base::db_service_base; 70 | }; 71 | 72 | empty_driver::empty_driver(empty_service& service) 73 | : _parent_ios{service.get_io_service()} 74 | { 75 | } 76 | 77 | } /* adio */ 78 | 79 | #endif // ADIO_EMPTY_DRIVER_HPP_INCLUDED 80 | -------------------------------------------------------------------------------- /source/drivers/postgresql/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_library(PQXX_LIBRARY NAMES libpqxx.a pqxx.lib) 2 | find_path(PQXX_INCLUDE_DIRECTORY pqxx/pqxx) 3 | 4 | if(NOT PQXX_LIBRARY) 5 | message(WARNING_AUTHOR "Did not find libpqxx for PostgreSQL support") 6 | return() 7 | endif() 8 | 9 | if(NOT PQXX_INCLUDE_DIRECTORY) 10 | message(WARNING_AUTHOR "Did not find pqxx include directories") 11 | return() 12 | endif() 13 | 14 | adio_backend(postgresql 15 | SOURCES 16 | adio/postgresql.hpp 17 | adio/postgresql.cpp 18 | LINK_LIBRARIES ${PQXX_LIBRARY} 19 | ) 20 | -------------------------------------------------------------------------------- /source/drivers/postgresql/adio/postgresql.cpp: -------------------------------------------------------------------------------- 1 | #include "postgresql.hpp" -------------------------------------------------------------------------------- /source/drivers/postgresql/adio/postgresql.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ADIO_POSTGRESQL_HPP_INCLUDED 2 | #define ADIO_POSTGRESQL_HPP_INCLUDED 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | namespace adio 10 | { 11 | 12 | class pg; 13 | 14 | namespace detail 15 | { 16 | 17 | class pg_service; 18 | } 19 | 20 | class pg 21 | { 22 | public: 23 | using service = detail::pg_service; 24 | using connection = basic_connection; 25 | 26 | using statement = int; 27 | 28 | pg(service&){} 29 | 30 | void open(boost::optional where = boost::none); 31 | void prepare(); 32 | void execute(); 33 | void step(); 34 | void close(); 35 | }; 36 | 37 | namespace detail 38 | { 39 | 40 | class pg_service : public db_service_base 41 | { 42 | public: 43 | using db_service_base::db_service_base; 44 | }; 45 | } 46 | } 47 | 48 | #endif // ADIO_POSTGRESQL_HPP_INCLUDED -------------------------------------------------------------------------------- /source/drivers/sqlite/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | adio_backend(sqlite 2 | SOURCES 3 | adio/sqlite.hpp 4 | adio/sqlite.cpp 5 | LINK_LIBRARIES 6 | sqlite::sqlite3 7 | ) 8 | -------------------------------------------------------------------------------- /source/drivers/sqlite/adio/sqlite.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | 7 | using namespace adio; 8 | using detail::sqlite_statement; 9 | using detail::sqlite_service; 10 | 11 | namespace adio 12 | { 13 | 14 | namespace detail 15 | { 16 | 17 | class sqlite_category : public asio_error_category 18 | { 19 | const char* name() const noexcept override { return "adio::sqlite"; } 20 | string message(int e) const override { return ::sqlite3_errstr(e); } 21 | }; 22 | 23 | struct sqlite_private 24 | { 25 | ::sqlite3* db = nullptr; 26 | ~sqlite_private() 27 | { 28 | if (db) ::sqlite3_close(db); 29 | } 30 | }; 31 | 32 | struct sqlite_statement_private 33 | { 34 | ::sqlite3_stmt* st = nullptr; 35 | ~sqlite_statement_private() 36 | { 37 | if (st) ::sqlite3_finalize(st); 38 | } 39 | }; 40 | 41 | } /* detail */ 42 | 43 | } /* adio */ 44 | 45 | const asio_error_category& adio::sqlite_category() 46 | { 47 | static detail::sqlite_category cat; 48 | return cat; 49 | } 50 | 51 | sqlite::sqlite(service& service) 52 | : _parent_ios{service.get_io_service()} 53 | , _service{service} 54 | , _private{new detail::sqlite_private} 55 | { 56 | } 57 | 58 | sqlite_statement::sqlite_statement() = default; 59 | sqlite_statement::~sqlite_statement() = default; 60 | sqlite_statement::sqlite_statement( 61 | std::shared_ptr&& p) 62 | : _private{std::move(p)} 63 | { 64 | } 65 | 66 | bool sqlite_statement::_advance() 67 | { 68 | auto rc = ::sqlite3_step(_private->st); 69 | switch (rc) 70 | { 71 | case SQLITE_DONE: 72 | _done = true; 73 | return true; 74 | case SQLITE_ROW: 75 | return false; 76 | case SQLITE_MISUSE: 77 | std::terminate(); 78 | default: 79 | throw system_error(make_error_code(static_cast(rc)), 80 | "Error advancing prepared statement"); 81 | } 82 | } 83 | 84 | void sqlite_statement::execute(error_code& ec) 85 | { 86 | while (1) 87 | { 88 | auto rc = ::sqlite3_step(_private->st); 89 | switch (rc) 90 | { 91 | case SQLITE_DONE: 92 | _done = true; 93 | return; 94 | case SQLITE_ROW: 95 | ec = make_error_code(static_cast(SQLITE_OK)); 96 | return; 97 | default: 98 | ec = make_error_code(static_cast(rc)); 99 | return; 100 | } 101 | } 102 | } 103 | 104 | sqlite::sqlite(sqlite&&) = default; 105 | sqlite& sqlite::operator=(sqlite&&) = default; 106 | 107 | sqlite::~sqlite() { close(); } 108 | 109 | error_code sqlite::open(const string& path) 110 | { 111 | close(); 112 | const auto err = ::sqlite3_open(path.data(), &_private->db); 113 | if (err != SQLITE_OK) return make_error_code(static_cast(err)); 114 | return {}; 115 | } 116 | 117 | std::shared_ptr 118 | sqlite::_prepare(const string& str, error_code& e) const 119 | { 120 | if (!_private->db) 121 | { 122 | e = make_error_code(adio::sys_errc::not_connected); 123 | return {}; 124 | } 125 | auto p = detail::make_unique(); 126 | auto err = ::sqlite3_prepare_v2(_private->db, 127 | str.data(), 128 | str.size(), 129 | &p->st, 130 | nullptr); 131 | if (err) 132 | { 133 | if (err != SQLITE_OK) 134 | e = make_error_code(static_cast(err)); 135 | return nullptr; 136 | } 137 | return {std::move(p)}; 138 | } 139 | 140 | std::vector sqlite::_multi_prepare(const string& source, 141 | error_code& e) const 142 | { 143 | if (!_private->db) 144 | { 145 | e = make_error_code(adio::sys_errc::not_connected); 146 | return {}; 147 | } 148 | auto cur_ptr = source.data(); 149 | auto remaining = source.size(); 150 | std::vector ret; 151 | while (cur_ptr) 152 | { 153 | auto next_ptr = cur_ptr; 154 | auto p = detail::make_unique(); 155 | auto err = ::sqlite3_prepare_v2(_private->db, 156 | cur_ptr, 157 | remaining, 158 | &p->st, 159 | &next_ptr); 160 | if (err != SQLITE_OK) 161 | { 162 | e = make_error_code(static_cast(err)); 163 | return {}; 164 | } 165 | ret.emplace_back(std::move(p)); 166 | if (next_ptr == nullptr) break; 167 | // There's another statement to compile. Go around again 168 | remaining -= std::distance(cur_ptr, next_ptr); 169 | cur_ptr = next_ptr; 170 | } 171 | return ret; 172 | } 173 | 174 | void sqlite::close() 175 | { 176 | if (_private->db) 177 | { 178 | ::sqlite3_close(_private->db); 179 | _private->db = nullptr; 180 | } 181 | } 182 | 183 | sqlite_service::sqlite_service(io_service& ios) 184 | : super_type{ios} 185 | , _my_ios{std::thread::hardware_concurrency() * 2} 186 | , _workptr{new io_service::work{_my_ios}} 187 | { 188 | } 189 | 190 | sqlite_service::~sqlite_service() 191 | { 192 | _workptr.reset(); 193 | _my_ios.stop(); 194 | for (auto& t : _threads) t.join(); 195 | } 196 | 197 | void sqlite_service::_ensure_threads_started() 198 | { 199 | std::call_once(_start_threads_flag, [this] { 200 | _threads.resize(std::thread::hardware_concurrency()); 201 | for (auto& t : _threads) t = std::thread{[this] { _my_ios.run(); }}; 202 | }); 203 | } 204 | 205 | void sqlite_service::_start_task(std::function task) 206 | { 207 | _ensure_threads_started(); 208 | _my_ios.post(std::move(task)); 209 | } 210 | 211 | row sqlite_statement::current_row() const 212 | { 213 | const auto pst = _private->st; 214 | const auto num_columns = ::sqlite3_column_count(pst); 215 | std::vector values; 216 | values.reserve(num_columns); 217 | 218 | for (auto i = 0; i < num_columns; ++i) 219 | { 220 | const auto sql_type = ::sqlite3_column_type(pst, i); 221 | 222 | switch (sql_type) 223 | { 224 | case SQLITE_INTEGER: 225 | values.emplace_back(value::integer{::sqlite3_column_int64(pst, i)}); 226 | break; 227 | case SQLITE_FLOAT: 228 | values.emplace_back(::sqlite3_column_double(pst, i)); 229 | break; 230 | case SQLITE_TEXT: 231 | { 232 | const auto ptr = ::sqlite3_column_text(pst, i); 233 | const auto len = ::sqlite3_column_bytes(pst, i); 234 | std::string str{ptr, ptr + len}; 235 | values.emplace_back(std::move(str)); 236 | break; 237 | } 238 | case SQLITE_BLOB: 239 | { 240 | const auto ptr 241 | = reinterpret_cast(::sqlite3_column_blob(pst, i)); 242 | const auto len = ::sqlite3_column_bytes(pst, i); 243 | std::vector data{ptr, ptr + len}; 244 | values.emplace_back(std::move(data)); 245 | break; 246 | } 247 | case SQLITE_NULL: 248 | values.emplace_back(nullptr); 249 | break; 250 | default: 251 | assert(0); 252 | } 253 | } 254 | 255 | return row{std::move(values)}; 256 | } 257 | 258 | void sqlite_statement::bind(int index, const value& value) 259 | { 260 | const auto pst = _private->st; 261 | int rc = SQLITE_OK; 262 | switch (value.get_type()) 263 | { 264 | case type::null_t: 265 | rc = ::sqlite3_bind_null(pst, index); 266 | break; 267 | case type::integer: 268 | rc = ::sqlite3_bind_int64(pst, index, value.get()); 269 | break; 270 | case type::real: 271 | rc = ::sqlite3_bind_double(pst, index, value.get()); 272 | break; 273 | case type::text: 274 | { 275 | const auto& str = value.get(); 276 | rc = ::sqlite3_bind_text(pst, 277 | index, 278 | str.data(), 279 | str.size(), 280 | SQLITE_TRANSIENT); 281 | break; 282 | } 283 | case type::blob: 284 | { 285 | const auto& bytes = value.get(); 286 | rc = ::sqlite3_bind_blob(pst, 287 | index, 288 | bytes.data(), 289 | bytes.size(), 290 | SQLITE_TRANSIENT); 291 | break; 292 | } 293 | case type::datetime: 294 | { 295 | const value::integer time 296 | = value.get().time_since_epoch().count(); 297 | rc = ::sqlite3_bind_int64(pst, index, time); 298 | break; 299 | } 300 | default: 301 | assert(0); 302 | std::terminate(); 303 | } 304 | switch (rc) 305 | { 306 | case SQLITE_NOMEM: 307 | throw std::bad_alloc{}; 308 | case SQLITE_MISUSE: 309 | std::terminate(); 310 | case SQLITE_RANGE: 311 | throw std::out_of_range{"Parameter index out of range"}; 312 | case SQLITE_TOOBIG: 313 | throw std::domain_error{"Parameter is too big"}; 314 | case SQLITE_OK: 315 | return; 316 | default: 317 | throw system_error(make_error_code(static_cast(rc)), 318 | "Failed to bind parameter"); 319 | } 320 | } 321 | 322 | void sqlite_statement::bind(const std::string& name, const value& value) 323 | { 324 | auto nparam = ::sqlite3_bind_parameter_count(_private->st); 325 | assert(nparam > 0); 326 | const auto index 327 | = ::sqlite3_bind_parameter_index(_private->st, name.data()); 328 | if (index == 0) throw std::domain_error{"No such parameter: " + name}; 329 | bind(index, value); 330 | } 331 | -------------------------------------------------------------------------------- /source/drivers/sqlite/adio/sqlite.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ADIO_SQLITE_DRIVER_HPP_INCLUDED 2 | #define ADIO_SQLITE_DRIVER_HPP_INCLUDED 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | namespace adio 15 | { 16 | 17 | /// Enumeration of SQLite error codes. See the SQLite documentation for an 18 | /// explanation of each 19 | enum class sqlite_errc 20 | { 21 | abort = 4, 22 | auth = 23, 23 | busy = 5, 24 | cant_open = 14, 25 | constraint = 19, 26 | corrupt = 11, 27 | done = 101, 28 | empty = 16, 29 | error = 1, 30 | format = 24, 31 | full = 13, 32 | internal = 2, 33 | interrupt = 9, 34 | ioerr = 10, 35 | locked = 6, 36 | mismatch = 20, 37 | misuse = 21, 38 | nolfs = 22, 39 | no_memory = 7, 40 | not_a_database = 26, 41 | not_found = 12, 42 | notice = 27, 43 | ok = 0, 44 | perm = 3, 45 | protocol = 15, 46 | range = 25, 47 | readonly = 8, 48 | row = 100, 49 | schema = 17, 50 | too_big = 18, 51 | warning = 28, 52 | 53 | abort_rollback = 516, 54 | busy_recovery = 261, 55 | busy_snapshot = 517, 56 | cant_open_convert_path = 1038, 57 | cant_open_full_path = 782, 58 | cant_open_is_directory = 526, 59 | cant_open_no_temp_directory = 270, 60 | constraint_check = 275, 61 | constraint_commit_hook = 531, 62 | constraint_foreign_key = 787, 63 | constraint_function = 1043, 64 | constraint_not_null = 1299, 65 | constraint_primary_key = 1555, 66 | constraint_rowid = 2579, 67 | constraint_trigger = 1811, 68 | constraint_unique = 2067, 69 | constraint_vtab = 2323, 70 | corrubt_vtab = 267, 71 | ioerr_access = 3338, 72 | ioerr_blocked = 2826, 73 | ioerr_check_reserved_lock = 3594, 74 | ioerr_close = 4106, 75 | ioerr_convert_path = 6666, 76 | ioerr_delete = 2570, 77 | ioerr_delete_noent = 5898, 78 | ioerr_dir_close = 4362, 79 | ioerr_dir_fsync = 1290, 80 | ioerr_fstat = 1802, 81 | ioerr_fsync = 1034, 82 | ioerr_gettemppath = 6410, 83 | ioerr_lock = 3850, 84 | ioerr_mmap = 6154, 85 | ioerr_nomem = 3082, 86 | ioerr_rdlock = 2314, 87 | ioerr_read = 266, 88 | ioerr_seek = 5642, 89 | ioerr_shmlock = 5130, 90 | ioerr_shmmap = 5386, 91 | ioerr_shmopen = 4618, 92 | ioerr_shmsize = 4874, 93 | ioerr_short_read = 522, 94 | ioerr_truncate = 1546, 95 | ioerr_unlock = 2058, 96 | ioerr_write = 778, 97 | locked_sharedcache = 262, 98 | notive_recover_rollback = 539, 99 | notice_recover_wal = 283, 100 | readonly_cantlock = 520, 101 | readonly_dbmoved = 1032, 102 | readonly_recovery = 264, 103 | readonly_rollback = 776, 104 | warning_autoindex = 284, 105 | }; 106 | 107 | /// Obtain an instance of the SQLite error category. 108 | extern const asio_error_category& sqlite_category(); 109 | 110 | inline error_code make_error_code(sqlite_errc e) 111 | { 112 | return adio::error_code{static_cast(e), sqlite_category()}; 113 | } 114 | 115 | inline error_condition make_error_condition(sqlite_errc e) 116 | { 117 | return {static_cast(e), sqlite_category()}; 118 | } 119 | 120 | namespace detail 121 | { 122 | 123 | struct sqlite_private; 124 | 125 | class sqlite_statement_private; 126 | class sqlite_statement; 127 | 128 | class sqlite_statement 129 | { 130 | std::shared_ptr _private; 131 | friend class sqlite_row_iterator; 132 | 133 | bool _advance(); 134 | 135 | bool _done = false; 136 | 137 | public: 138 | sqlite_statement(); 139 | explicit sqlite_statement(std::shared_ptr&& p); 140 | ~sqlite_statement(); 141 | sqlite_statement(sqlite_statement&&) = default; 142 | sqlite_statement(const sqlite_statement&) = delete; 143 | sqlite_statement& operator=(sqlite_statement&&) = default; 144 | sqlite_statement& operator=(const sqlite_statement&) = delete; 145 | 146 | using row = adio::row; 147 | 148 | row current_row() const; 149 | 150 | class iterator : public std::iterator 151 | { 152 | std::reference_wrapper _st; 153 | bool _is_end = false; 154 | void _advance(); 155 | 156 | public: 157 | iterator(sqlite_statement& st, bool end) 158 | : _st{st} 159 | , _is_end{end} 160 | { 161 | if (!end) ++*this; 162 | } 163 | 164 | iterator& operator++() 165 | { 166 | _is_end = _st.get()._advance(); 167 | return *this; 168 | } 169 | 170 | bool operator!=(const iterator& other) 171 | { 172 | return other._is_end != _is_end; 173 | } 174 | 175 | row operator*() const { return _st.get().current_row(); } 176 | }; 177 | 178 | iterator begin() { return {*this, false}; }; 179 | iterator end() { return {*this, true}; } 180 | 181 | void execute() 182 | { 183 | error_code ec; 184 | execute(ec); 185 | detail::throw_if_error(ec, "Failed to execute prepared statement"); 186 | } 187 | void execute(error_code& ec); 188 | 189 | void bind(int index, const value& value); 190 | void bind(const std::string& name, const value& value); 191 | 192 | bool done() const { return _done; } 193 | }; 194 | 195 | class sqlite_service; 196 | 197 | } /* detail */ 198 | 199 | class sqlite; 200 | 201 | class sqlite : public std::enable_shared_from_this 202 | { 203 | public: 204 | using statement = detail::sqlite_statement; 205 | using row = statement::row; 206 | using connection = basic_connection; 207 | using service = detail::sqlite_service; 208 | 209 | private: 210 | std::reference_wrapper _parent_ios; 211 | std::reference_wrapper _service; 212 | std::unique_ptr _private; 213 | 214 | template void _push_task(Task&& task); 215 | 216 | std::shared_ptr 217 | _prepare(const string&, error_code&) const; 218 | 219 | std::vector _multi_prepare(const string&, error_code&) const; 220 | 221 | public: 222 | sqlite(service& service); 223 | sqlite(sqlite&&); 224 | sqlite& operator=(sqlite&&); 225 | ~sqlite(); 226 | 227 | void close(); 228 | 229 | using open_handler_signature = void(error_code); 230 | error_code open(const string&); 231 | template 232 | void async_open(const string& path, Handler&& handler) 233 | { 234 | auto this_pin = shared_from_this(); 235 | _push_task([ 236 | this_pin, 237 | work_pin = detail::make_work(_parent_ios), 238 | this, 239 | path, 240 | handler = std::forward(handler) 241 | ] { 242 | auto ec = open(path); 243 | _parent_ios.get().post(std::bind(handler, ec)); 244 | }); 245 | } 246 | 247 | using prepare_handler_signature = void(statement, error_code); 248 | statement prepare(const string& str) 249 | { 250 | error_code ec; 251 | auto st = prepare(str, ec); 252 | detail::throw_if_error(ec, 253 | "Failed to prepare statement: \"" + str + "\""); 254 | return st; 255 | } 256 | statement prepare(const string& query, error_code& ec) 257 | { 258 | return statement{_prepare(query, ec)}; 259 | } 260 | using prepare_multiple_handler_signature 261 | = void(std::vector, error_code); 262 | std::vector prepare_multiple(const string& source) 263 | { 264 | error_code ec; 265 | auto sts = prepare_multiple(source, ec); 266 | detail::throw_if_error(ec, 267 | "Failed to prepare SQL source: \"" + source 268 | + "\""); 269 | return sts; 270 | } 271 | 272 | std::vector prepare_multiple(const string& source, 273 | error_code& ec) 274 | { 275 | return _multi_prepare(source, ec); 276 | } 277 | 278 | template 279 | void async_prepare(const string& query, Handler&& handler) 280 | { 281 | _push_task([ 282 | this_pin = shared_from_this(), 283 | work_pin = detail::make_work(_parent_ios), 284 | this, 285 | query, 286 | handler = std::forward(handler) 287 | ] { 288 | error_code err; 289 | auto st = _prepare(query, err); 290 | _parent_ios.get().post([st, err, handler]() mutable { 291 | handler(statement{std::move(st)}, err); 292 | }); 293 | }); 294 | } 295 | 296 | using execute_handler_signature = void(error_code); 297 | void execute(statement&& st) { execute(st); } 298 | void execute(statement& st) 299 | { 300 | error_code ec; 301 | execute(st, ec); 302 | detail::throw_if_error(ec, "Failed to execute prepared statement"); 303 | } 304 | void execute(statement&& st, error_code& ec) { execute(st, ec); } 305 | void execute(statement& st, error_code& ec) { st.execute(ec); } 306 | template 307 | void async_execute(statement& st, Handler&& handler) 308 | { 309 | _push_task([ 310 | this_pin = shared_from_this(), 311 | work_pin = detail::make_work(_parent_ios), 312 | this, 313 | st_ref = std::ref(st), 314 | handler = std::forward(handler) 315 | ]() mutable { 316 | error_code ec; 317 | execute(st_ref, ec); 318 | _parent_ios.get().post(std::bind(handler, ec)); 319 | }); 320 | } 321 | 322 | void execute(const string& query) 323 | { 324 | error_code ec; 325 | execute(query, ec); 326 | detail::throw_if_error(ec, "Failed to execute query: " + query); 327 | } 328 | void execute(const string& query, error_code& ec) 329 | { 330 | ec = {}; 331 | auto st = prepare(query, ec); 332 | if (ec) return; 333 | execute(st, ec); 334 | } 335 | template 336 | void async_execute(const string& query, Handler&& handler) 337 | { 338 | async_prepare(query, [ 339 | this_pin = shared_from_this(), 340 | work_pin = detail::make_work(_parent_ios), 341 | this, 342 | handler = std::forward(handler) 343 | ](statement st, error_code ec) { 344 | if (ec) 345 | { 346 | _parent_ios.get().post(std::bind(handler, ec)); 347 | return; 348 | } 349 | async_execute([ 350 | this_pin, 351 | work_pin, 352 | this, 353 | handler = std::forward(handler) 354 | ](error_code ec) { 355 | _parent_ios.get().post(std::bind(handler, ec)); 356 | }); 357 | }); 358 | } 359 | 360 | using step_handler_signature = void(row, error_code); 361 | row step(statement& st) 362 | { 363 | error_code ec; 364 | const auto result = step(st, ec); 365 | detail::throw_if_error(ec, "Failed to step query"); 366 | return result; 367 | } 368 | row step(statement& st, error_code& ec) 369 | { 370 | ec = {}; 371 | st.execute(ec); 372 | if (ec || st.done()) return row({}); 373 | return st.current_row(); 374 | } 375 | template void async_step(statement& st, Handler&& h) 376 | { 377 | _push_task([ 378 | this_pin = shared_from_this(), 379 | work_pin = detail::make_work(_parent_ios), 380 | this, 381 | st_ref = std::ref(st), 382 | handler = std::forward(h) 383 | ]() mutable { 384 | error_code ec; 385 | step(st_ref, ec); 386 | _parent_ios.get().post(std::bind(handler, ec)); 387 | }); 388 | } 389 | }; 390 | 391 | namespace detail 392 | { 393 | 394 | class sqlite_service : public db_service_base 395 | { 396 | public: 397 | using self_type = sqlite_service; 398 | using super_type = db_service_base; 399 | 400 | private: 401 | friend class adio::sqlite; 402 | 403 | io_service _my_ios; 404 | std::unique_ptr _workptr; 405 | std::once_flag _start_threads_flag; 406 | std::vector _threads; 407 | 408 | void _ensure_threads_started(); 409 | void _start_task(std::function); 410 | 411 | template void _push_task(Task&& task) 412 | { 413 | std::function pt{std::forward(task)}; 414 | _start_task(std::move(pt)); 415 | } 416 | 417 | public: 418 | sqlite_service(io_service&); 419 | ~sqlite_service(); 420 | 421 | using connection_type = sqlite; 422 | }; 423 | 424 | } /* detail */ 425 | 426 | template void sqlite::_push_task(Task&& task) 427 | { 428 | _service.get()._push_task(std::forward(task)); 429 | } 430 | 431 | } /* adio */ 432 | 433 | ADIO_DECLARE_ERRC_ENUM(adio::sqlite_errc); 434 | 435 | #endif // ADIO_SQLITE_DRIVER_HPP_INCLUDED 436 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | foreach(backend empty sqlite postgresql) 3 | if(TARGET adio::${backend}) 4 | list(APPEND backend_tests ${backend}) 5 | endif() 6 | endforeach() 7 | 8 | foreach(test connection value row ${backend_tests}) 9 | add_executable(test.${test} ${test}.cpp) 10 | catch_add_tests(adio test.${test}) 11 | target_link_libraries(test.${test} PUBLIC adio boost::coroutine boost::thread) 12 | if(TARGET adio::${test}) 13 | target_link_libraries(test.${test} PRIVATE adio::${test}) 14 | endif() 15 | endforeach() 16 | -------------------------------------------------------------------------------- /tests/connection.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | 6 | TEST_CASE("Create a connection") 7 | { 8 | adio::asio::io_service ios; 9 | } 10 | -------------------------------------------------------------------------------- /tests/empty.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | TEST_CASE("Create empty driver") 9 | { 10 | adio::asio::io_service ios; 11 | adio::empty_driver::connection db{ios}; 12 | db.async_open("foo", []{}); 13 | db.execute("SELECT * FROM stuff"); 14 | 15 | bool did_run = true; 16 | db.async_execute("foo", [&] { std::cout << "We did a thing!\n"; }); 17 | 18 | bool did_run2 = true; 19 | adio::asio::spawn(ios, [&](adio::asio::yield_context yc) { 20 | db.async_execute("SELECT foo FROM bars", yc); 21 | did_run2 = true; 22 | std::cout << "Coroutine returned\n"; 23 | }); 24 | 25 | ios.run(); 26 | CHECK(did_run); 27 | CHECK(did_run2); 28 | } 29 | -------------------------------------------------------------------------------- /tests/postgresql.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | 7 | TEST_CASE("Open a connection") 8 | { 9 | adio::io_service ios; 10 | adio::pg::connection conn{ ios }; 11 | } -------------------------------------------------------------------------------- /tests/row.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | using adio::row; 6 | 7 | -------------------------------------------------------------------------------- /tests/sqlite.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #define DECL_CON \ 9 | adio::io_service ios; \ 10 | adio::sqlite::connection con { ios } 11 | #define DECL_OPEN \ 12 | DECL_CON; \ 13 | con.open("foo.db") 14 | 15 | TEST_CASE("Open SQLite database") 16 | { 17 | DECL_CON; 18 | auto ec = con.open("test.db"); 19 | CHECK_FALSE(ec); 20 | } 21 | 22 | TEST_CASE("Async open SQLite database") 23 | { 24 | DECL_CON; 25 | bool worked = false; 26 | con.async_open("test.db", [&](adio::error_code) { worked = true; }); 27 | ios.run(); 28 | CHECK(worked); 29 | } 30 | 31 | TEST_CASE("Spawn open SQLite database") 32 | { 33 | DECL_CON; 34 | bool worked = false; 35 | adio::asio::spawn(ios, [&](adio::asio::yield_context yc) { 36 | auto ec = make_error_code(adio::sqlite_errc::ioerr_read); 37 | con.async_open("test.db", yc[ec]); 38 | CHECK_FALSE(ec); 39 | worked = true; 40 | }); 41 | ios.run(); 42 | CHECK(worked); 43 | } 44 | 45 | TEST_CASE("Bad open sqlite database") 46 | { 47 | DECL_CON; 48 | auto ec = con.open("nonexistent-directory/foo.db"); 49 | CHECK(ec); 50 | } 51 | 52 | TEST_CASE("Bad async open sqlite database") 53 | { 54 | DECL_CON; 55 | adio::error_code ec; 56 | CHECK_FALSE(ec); 57 | con.async_open("nonexistent-dir/foo.db", 58 | [&](adio::error_code err) { ec = err; }); 59 | ios.run(); 60 | CHECK(ec); 61 | } 62 | 63 | TEST_CASE("Bad spawn open sqlite database") 64 | { 65 | DECL_CON; 66 | adio::error_code ec; 67 | adio::asio::spawn(ios, [&](adio::asio::yield_context yc) { 68 | con.async_open("nonexistent-dir/foo.db", yc[ec]); 69 | }); 70 | CHECK_FALSE(ec); 71 | ios.run(); 72 | CHECK(ec); 73 | } 74 | 75 | TEST_CASE("Bad spawn open with exception") 76 | { 77 | DECL_CON; 78 | adio::asio::spawn(ios, [&](adio::asio::yield_context yc) { 79 | try 80 | { 81 | con.async_open("nonexistent-dir/foo.db", yc); 82 | CHECK(false); 83 | } 84 | catch (adio::system_error& e) 85 | { 86 | CHECK(e.code() == adio::sqlite_errc::cant_open); 87 | } 88 | }); 89 | ios.run(); 90 | } 91 | 92 | TEST_CASE("SQLite prepare") 93 | { 94 | DECL_OPEN; 95 | con.prepare( 96 | "CREATE TABLE IF NOT EXISTS myTable (id INTEGER PRIMARY KEY, name " 97 | "VARCHAR(1024))"); 98 | } 99 | 100 | 101 | TEST_CASE("SQLite async prepare") 102 | { 103 | DECL_OPEN; 104 | bool did_call = false; 105 | con.async_prepare( 106 | "CREATE TABLE IF NOT EXISTS myTable (id INTEGER PRIMARY KEY, name " 107 | "VARCHAR(1024))", 108 | [&](adio::sqlite::connection::statement, adio::error_code) { 109 | did_call = true; 110 | }); 111 | ios.run(); 112 | CHECK(did_call); 113 | } 114 | 115 | 116 | TEST_CASE("Bad SQLite prepare") 117 | { 118 | DECL_OPEN; 119 | try 120 | { 121 | con.prepare( 122 | "CREATE WHERE TABLE IF NOT EXISTS myTable (id INTEGER PRIMARY KEY, " 123 | "name VARCHAR(1024))"); 124 | CHECK(false); 125 | } 126 | catch (adio::system_error& e) 127 | { 128 | CHECK(e.code() == adio::sqlite_errc::error); 129 | } 130 | } 131 | 132 | 133 | TEST_CASE("Bad SQLite async prepare") 134 | { 135 | DECL_OPEN; 136 | bool did_call = false; 137 | con.async_prepare( 138 | "CREATE WHERE TABLE IF NOT EXISTS myTable (id INTEGER PRIMARY KEY, " 139 | "name VARCHAR(1024))", 140 | [&](adio::sqlite::connection::statement, adio::error_code e) { 141 | did_call = true; 142 | CHECK(e == adio::sqlite_errc::error); 143 | }); 144 | ios.run(); 145 | CHECK(did_call); 146 | } 147 | 148 | 149 | TEST_CASE("SQLite execute") 150 | { 151 | DECL_OPEN; 152 | con.execute( 153 | "CREATE TABLE IF NOT EXISTS myTable (id INTEGER PRIMARY KEY, name " 154 | "VARCHAR(1024))"); 155 | } 156 | 157 | 158 | TEST_CASE("Bad SQLite execute") 159 | { 160 | adio::io_service ios; 161 | adio::sqlite::connection con{ios}; 162 | adio::error_code ec; 163 | con.execute("CREATE FOO BLARGH", ec); 164 | CHECK(ec); 165 | } 166 | 167 | 168 | TEST_CASE("Iterate over data") 169 | { 170 | DECL_OPEN; 171 | con.prepare("DROP TABLE IF EXISTS myTable").execute(); 172 | con.prepare( 173 | "CREATE TABLE IF NOT EXISTS myTable(id INTEGER PRIMARY KEY, name " 174 | "VARCHAR(1024))") 175 | .execute(); 176 | con.prepare("INSERT INTO myTable (name) VALUES ('Hats')").execute(); 177 | con.prepare("INSERT INTO myTable (name) VALUES ('Hats')").execute(); 178 | con.prepare("INSERT INTO myTable (name) VALUES ('Hats')").execute(); 179 | auto st = con.prepare("SELECT * FROM myTable"); 180 | int count = 0; 181 | for (const auto& item : st) 182 | { 183 | count++; 184 | CHECK(item[0] == count); 185 | CHECK(item[1] == "Hats"); 186 | CHECK(item.size() == 2); 187 | } 188 | CHECK(count == 3); 189 | } 190 | 191 | 192 | TEST_CASE("Step over data") 193 | { 194 | DECL_OPEN; 195 | auto st = con.prepare("SELECT * from myTable"); 196 | for (auto row = con.step(st); !st.done(); row = con.step(st)) 197 | { 198 | CHECK(row.size()); 199 | } 200 | } 201 | 202 | 203 | TEST_CASE("Bind indexed parameters") 204 | { 205 | DECL_OPEN; 206 | con.execute("DROP TABLE IF EXISTS hats"); 207 | con.execute(R"( 208 | CREATE TABLE IF NOT EXISTS hats ( 209 | id INTEGER PRIMARY KEY NOT NULL, 210 | name TEXT NOT NULL, 211 | color VARCHAR(16) NOT NULL 212 | ))"); 213 | con.execute("INSERT INTO hats (name, color) VALUES ('top', 'black')"); 214 | auto st = con.prepare("SELECT name FROM hats WHERE color = ?"); 215 | st.bind(1, adio::value{"black"}); 216 | std::vector rows{ begin(st), end(st) }; 217 | REQUIRE(rows.size() == 1); 218 | REQUIRE(rows[0].size() == 1); 219 | CHECK(rows[0][0] == "top"); 220 | } 221 | 222 | 223 | TEST_CASE("Bind named parameters") 224 | { 225 | DECL_OPEN; 226 | con.execute("DROP TABLE IF EXISTS hats"); 227 | con.execute(R"( 228 | CREATE TABLE IF NOT EXISTS hats ( 229 | id INTEGER PRIMARY KEY NOT NULL, 230 | type TEXT NOT NULL, 231 | color VARCHAR(16) NOT NULL 232 | ))"); 233 | con.execute("INSERT INTO hats (type, color) VALUES ('top', 'black')"); 234 | con.execute("INSERT INTO hats (type, color) VALUES ('top', 'green')"); 235 | auto st = con.prepare("SELECT type FROM hats WHERE color = :color"); 236 | st.bind(":color", adio::value{ "black" }); 237 | std::vector rows{ begin(st), end(st) }; 238 | REQUIRE(rows.size() == 1); 239 | CHECK(rows[0] == "top"); 240 | } 241 | -------------------------------------------------------------------------------- /tests/value.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | using adio::value; 6 | 7 | TEST_CASE("Null values") 8 | { 9 | value v; 10 | auto b = v == adio::null; 11 | CHECK(b); 12 | auto b2 = v.get_type() == adio::type::null_t; 13 | CHECK(b2); 14 | CHECK(v == v); 15 | CHECK_FALSE(v != v); 16 | CHECK_FALSE(v < v); 17 | CHECK(v <= v); 18 | CHECK(v >= v); 19 | CHECK_FALSE(v > v); 20 | } 21 | 22 | TEST_CASE("Integer values") 23 | { 24 | value v{value::integer{12}}; 25 | CHECK(v == 12); 26 | CHECK(v < 14); 27 | CHECK(v != 13); 28 | CHECK(v > 9); 29 | CHECK(v != "Dogs"); 30 | } 31 | 32 | TEST_CASE("String values") 33 | { 34 | value v = "Hello!"; 35 | CHECK(v == "Hello!"); 36 | CHECK(v != "World"); 37 | CHECK(v < "World"); 38 | CHECK(v > "Apple"); 39 | } 40 | 41 | TEST_CASE("Conversion") 42 | { 43 | value v = 128; 44 | CHECK_THROWS_AS(v.get(), adio::invalid_access); 45 | CHECK(v.get() == 128); 46 | CHECK(v.get() == 128); 47 | CHECK(value::integer(v) == 128); 48 | // Check bool conversion 49 | CHECK(v); 50 | } 51 | 52 | TEST_CASE("String conversion") 53 | { 54 | value v = "Hello, world!"; 55 | CHECK_THROWS_AS(v.get(), adio::invalid_access); 56 | char arr[] = "Hello, world!"; 57 | CHECK(v.get>() 58 | == std::basic_string(std::begin(arr), std::end(arr)-1)); 59 | } 60 | 61 | struct MyString 62 | { 63 | std::string str; 64 | }; 65 | 66 | struct NullableString 67 | { 68 | MyString data; 69 | bool null; 70 | }; 71 | 72 | namespace adio 73 | { 74 | 75 | template<> struct value_adaptor 76 | { 77 | using base_type = std::string; 78 | enum { nullable = false }; 79 | static MyString convert(const base_type& t) 80 | { 81 | return MyString{t}; 82 | } 83 | 84 | static base_type convert(const MyString& t) 85 | { 86 | return t.str; 87 | } 88 | }; 89 | 90 | template<> struct value_adaptor 91 | { 92 | enum { nullable = true }; 93 | using base_type = MyString; 94 | static NullableString null() { return NullableString{"", true}; } 95 | static bool is_null(const NullableString& ns) { return ns.null; } 96 | static NullableString convert(const base_type& t) { return NullableString{t, false}; } 97 | static base_type convert(const NullableString& ns) { return ns.data; } 98 | }; 99 | 100 | } /* adio */ 101 | 102 | 103 | TEST_CASE("Custom types") 104 | { 105 | value v{ "Cats" }; 106 | CHECK(v.get().str == "Cats"); 107 | value other{ MyString{ "Dogs" } }; 108 | CHECK(other == "Dogs"); 109 | CHECK(other != 12); 110 | } 111 | 112 | TEST_CASE("Custom nullable types") 113 | { 114 | { 115 | value v{"Dogs"}; 116 | CHECK_FALSE(v.get().null); 117 | CHECK(v.get().data.str == "Dogs"); 118 | } 119 | 120 | { 121 | value v; 122 | CHECK(v.get().null); 123 | } 124 | 125 | { 126 | NullableString str{ "Cats", false }; 127 | CHECK(value{str} == "Cats"); 128 | } 129 | 130 | { 131 | NullableString nullstr{ "", true }; 132 | CHECK(value{nullstr} == adio::null); 133 | } 134 | } 135 | --------------------------------------------------------------------------------