diff --git a/.gitignore b/.gitignore index 0def275..5b404db 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,6 @@ *.exe *.out *.app + +# CMake build files +build/* diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..e634f42 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,30 @@ +language: cpp + +matrix: + include: + - os: linux + compiler: gcc + addons: + apt: + sources: ['ubuntu-toolchain-r-test', 'boost-latest'] + packages: ['g++-6', 'libboost1.54-dev'] + env: COMPILER=g++-6 + - os: linux + compiler: clang + addons: + apt: + sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.8', 'boost-latest'] + packages: ['g++-5', 'clang-3.8', 'libboost1.54-dev'] + env: COMPILER=clang++-3.8 + - os: osx + osx_image: xcode7.3 + compiler: clang + env: COMPILER=clang++ + +script: +- export CXX=${COMPILER} +- ${CXX} --version +- mkdir -p build && cd build +- cmake .. && make -j4 +- cd tests +- ctest --output-on-failure diff --git a/CMake/iod-config.cmake.in b/CMake/iod-config.cmake.in new file mode 100644 index 0000000..f6028be --- /dev/null +++ b/CMake/iod-config.cmake.in @@ -0,0 +1,9 @@ +@PACKAGE_INIT@ + +# Avoid repeatedly including the targets +if(NOT TARGET iod::iod) + # Provide path for scripts + list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") + include(${CMAKE_CURRENT_LIST_DIR}/iod-targets.cmake) + check_required_components("@PROJECT_NAME@") +endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index f333832..b6f2e55 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,34 +1,119 @@ -cmake_minimum_required(VERSION 2.8) -project(Iod) +cmake_minimum_required(VERSION 3.0.2) +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) +cmake_policy(SET CMP0048 NEW) +project(iod VERSION 1.0.0 LANGUAGES CXX) +cmake_policy(SET CMP0048 NEW) -install(DIRECTORY iod DESTINATION include - FILES_MATCHING PATTERN "*.hh") -install(DIRECTORY iod DESTINATION include - FILES_MATCHING PATTERN "*.hpp") +find_package(Boost REQUIRED) # For lexical_cast -add_subdirectory(tools) +set(CMAKE_CXX_STANDARD 14) -set(IOD_INCLUDE_DIR "include") -set(CMAKE_CONFIG_DEST "share/iod") +option(IOD_BUILD_TOOLS "Build command line iod_generate_symbols tool" ON) +option(IOD_BUILD_TESTS "Build ctest tests" ON) -include(CMakePackageConfigHelpers) -configure_package_config_file ( - ${CMAKE_SOURCE_DIR}/IodConfig.cmake.in - ${CMAKE_BINARY_DIR}/IodConfig.cmake - INSTALL_DESTINATION ${CMAKE_CONFIG_DEST} - PATH_VARS IOD_INCLUDE_DIR CMAKE_CONFIG_DEST) - -export(PACKAGE Iod) - -configure_file(IodConfigVersion.cmake.in - "${PROJECT_BINARY_DIR}/IodConfigVersion.cmake" @ONLY) - -install(FILES - "${PROJECT_SOURCE_DIR}/IodUse.cmake" - "${PROJECT_BINARY_DIR}/IodConfig.cmake" - "${PROJECT_BINARY_DIR}/IodConfigVersion.cmake" - DESTINATION share/iod) - -# Install the export set for use with the install-tree -install(EXPORT IodTargets DESTINATION ${CMAKE_CONFIG_DEST}) +if (IOD_BUILD_TOOLS) + add_subdirectory(tools) +endif() + +if (IOD_BUILD_TESTS) + add_subdirectory(tests) +endif() + +add_library(iod INTERFACE) +target_sources( + iod + INTERFACE + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $) + +target_include_directories( + iod + INTERFACE + $ + $ +) + +target_compile_features(${PROJECT_NAME} INTERFACE cxx_std_14) + +set(IOD_CMAKE_CONFIG_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/iod") + +# Manage version comprarison. +# +# iod is header only so it doesn't care about architecture so +# temporarily clear CMAKE_SIZEOF_VOIDP during the write_basic_package_version_file() +# call to make an architecture-independent version file. +set(IOD_CMAKE_SIZEOF_VOID_P ${CMAKE_SIZEOF_VOIDP}) +set(CMAKE_SIZEOF_VOIDP "") +write_basic_package_version_file( + "${PROJECT_BINARY_DIR}/iod-version.cmake" + COMPATIBILITY SameMajorVersion +) +set(CMAKE_SIZEOF_VOIDP ${IOD_CMAKE_SIZEOF_VOIDP}) + +# Generate configuration file which cmake uses for using an installed package. +configure_package_config_file( + ${PROJECT_SOURCE_DIR}/CMake/iod-config.cmake.in + ${PROJECT_BINARY_DIR}/iod-config.cmake + INSTALL_DESTINATION ${IOD_CMAKE_CONFIG_DESTINATION} +) + +# Set folders for installation artifacts - INCLUDE is omitted because +# target_include_directories() has the INSTALL_INTERFACE line. +install( + TARGETS iod + EXPORT iod-targets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) + +# Install iod-targets.cmake defined in share-ring-buffer-config.cmake +install( + EXPORT iod-targets + NAMESPACE ${PROJECT_NAME}:: + DESTINATION ${IOD_CMAKE_CONFIG_DESTINATION} +) + +# Install iod-config.cmake and iod-version.cmake +install( + FILES + "${PROJECT_BINARY_DIR}/iod-config.cmake" + "${PROJECT_BINARY_DIR}/iod-version.cmake" + DESTINATION "${IOD_CMAKE_CONFIG_DESTINATION}" +) + +# Finally, install the actual header files in this header-only library. +file(GLOB IOD_HEADER_FILES "${PROJECT_SOURCE_DIR}/iod/*.hh") +install( + FILES ${IOD_HEADER_FILES} + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/iod" +) diff --git a/IodConfig.cmake.in b/IodConfig.cmake.in index 7752896..aec62e3 100644 --- a/IodConfig.cmake.in +++ b/IodConfig.cmake.in @@ -7,5 +7,3 @@ set_and_check(IOD_INCLUDE_DIR "@PACKAGE_IOD_INCLUDE_DIR@") include("@PACKAGE_CMAKE_CONFIG_DEST@/IodTargets.cmake") - -set_and_check(IOD_USE_FILE "@PACKAGE_CMAKE_CONFIG_DEST@/IodUse.cmake") diff --git a/IodUse.cmake b/IodUse.cmake deleted file mode 100644 index f924304..0000000 --- a/IodUse.cmake +++ /dev/null @@ -1,41 +0,0 @@ -set(IOD_TMP_DIR "${CMAKE_CURRENT_BINARY_DIR}/iod_tmp_dir") -file(MAKE_DIRECTORY ${IOD_TMP_DIR}) -set(SYMBOL_INCLUDE_FILE ${IOD_TMP_DIR}/symbol_include.hh) -file(WRITE ${SYMBOL_INCLUDE_FILE} "#include \n") - -set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-std=c++14") - -function(add_iod_executable arg1) - - set(sources "") - foreach(iod_compile_rules ${ARGN}) - set(input_file ${CMAKE_CURRENT_SOURCE_DIR}/${iod_compile_rules}) - set(inputp_file ${IOD_TMP_DIR}/${iod_compile_rules}.i) - set(output_file ${IOD_TMP_DIR}/${iod_compile_rules}) - set(sources ${sources} ${output_file}) - - get_property(dirs DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES) - set(INCLUDE_FLAGS) - foreach(dir ${dirs}) - set(INCLUDE_FLAGS ${INCLUDE_FLAGS} -I ${dir} ) - endforeach() - - add_custom_command(OUTPUT ${inputp_file} - COMMAND ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_DEFINES} ${CMAKE_CXX_FLAGS} ${INCLUDE_FLAGS} -E ${input_file} > ${inputp_file} - IMPLICIT_DEPENDS CXX ${input_file}) - - add_custom_command(OUTPUT ${output_file} - COMMAND iodc ${inputp_file} ${output_file} - DEPENDS ${inputp_file}) - -# add_custom_command(OUTPUT ${output_file} -# COMMAND iodc ${input_file}.i ${output_file} -# #DEPENDS ${input_file}.i -# IMPLICIT_DEPENDS ${input_file}.i -# ) - - endforeach(iod_compile_rules) - #add_executable(${arg1}_just_for_preprocessor_rules ${ARGN}) - add_executable(${arg1} ${sources}) - -endfunction(add_iod_executable) diff --git a/README.md b/README.md index b6d9ddf..ba3ccf6 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,25 @@ +[![Travis build status](https://travis-ci.org/matt-42/iod.svg)](https://travis-ci.org/matt-42/iod) + +Important Note +======================= + +This project has been refactored and renamed as the [Lithium Libraries](https://github.com/matt-42/lithium): +https://github.com/matt-42/lithium + The IOD Library ========================== + The IOD library enhances C++14 meta programming with a symbol based -paradigm. It provides a compile-time a way to introspect objects and -generate code matching their data structures. +paradigm. It provides a compile-time way to introspect objects and +generate code matching their data structures. It also contains few +utilities built with symbol meta-programming. ## What is a IOD symbol? Symbols are at the core of the IOD paradigm. They add to C++ a missing powerful feature: The way to statically store in a variable the -access to a object member, the call to a method, and the access to the +access to an object member, the call to a method, and the access to the string representing the name of this variable. Here is the most simple but powerful operator that is now possible with IOD symbols: @@ -17,16 +27,14 @@ but powerful operator that is now possible with IOD symbols: #include #include -iod_define_symbol(a, _a); // Refer to members and methods a with symbol _a -iod_define_symbol(b, _b); // Refer to members and methods b with symbol _b -iod_define_symbol(c, _c); // Refer to members and methods c with symbol _c +iod_define_symbol(a); // Refer to members and methods a with symbol _a +iod_define_symbol(b); // Refer to members and methods b with symbol _b +iod_define_symbol(c); // Refer to members and methods c with symbol _c int main() { // Symbols are located in namespace s to avoid name clash. - using s::_a; - using s::_b; - using s::_c; + using s; auto print_member = [] (auto& obj, auto& m) { @@ -45,14 +53,14 @@ Without symbols (or other similar constructs), it is not possible to write such a generic print_member function. Without, one would have to write the three version accessing the three different members. -By convention all the symbols starts with _[uppercase character]. And -to avoid multiple definition, guards should be used such as in the +By convention all the symbols start with the _[lowercase character]. And +to avoid multiple definitions, guards should be used such as in the following: ```c++ -#ifndef IOD_SYMBOL__Mysymbol -#define IOD_SYMBOL__Mysymbol - iod_define_symbol(mysymbol, _mysymbol); +#ifndef IOD_SYMBOL_mysymbol +#define IOD_SYMBOL_mysymbol + iod_define_symbol(mysymbol); #endif } ``` @@ -89,10 +97,29 @@ Statically introspectable objects features are: << m.value() << std::end; } ``` +## Command line parser. + +Iod provides an easy to use, type safe command line parser : + +```c++ +const char* argv[] = {"", "--opt1" , "12", "--opt2", "abc"}; +int argc = 5; +auto opts = parse_command_line(argc, argv, + _opt1 = int(), + _opt2 = std::string()); +// With +// ./a.out --opt1 12 --opt2 abc +// It should give +// opts.opt1 == 12 +// opts.opt2 == "abc" +``` + +More example can be found in the test here: +https://github.com/matt-42/iod/blob/master/tests/parse_command_line.cc ## A Fast, Malloc-Free Json Parser / Encoder. -As of today, all json parsers rely dynamic data structures to +As of today, all json parsers rely upon dynamic data structures to parse and store the json objects. Even if some good parser reduces dramatically the amount of dynamic memory allocation, they still suffer from the dynamic paradigm: A single function has to handle the @@ -103,13 +130,13 @@ have to use a json parser handling any kind of objects. The iod library implements the opposite approach: Using meta-programming and introspection, one tiny specialized json parser is generated for each SIO object type so it involves no dynamic memory -allocation, and very few conditional branching. It directly fill the +allocation and very few conditional branching. It directly fills the object member according to their type without having to store the object in an intermediate hash map. This makes its performances impossible to match in other languages such as C or Java that do not provide static introspection. The -encoder still need optimizations, and the parser is currently from +encoder still needs optimizations, and the parser is currently from 1.3x to 2.3x faster than the RapidJSON library without explicit use of SIMD vector instructions. @@ -160,10 +187,10 @@ json_decode(u2, R"({"username":"John","city":22})"); In classic C++, you would define a function taking optional arguments as : ```c++ -void fun(int mandatory_arg, int optional_arg1 = 1, int optional_arg2 = 12, int optional_arg3 = 12); +void fun(int mandatory_arg, int optional_arg1 = 1, int optional_arg2 = 12, int optional_arg3 = 32); ``` -This has to drawbacks: First, it is not practical if the user need to +This has two drawbacks: First, it is not practical if the user needs to set the third optional argument, and oblige him to remember the place of each argument. SIOs are a good alternative since they solve both issues: @@ -173,8 +200,8 @@ void fun(int mandatory_arg, const O&... opts) { const auto options = D(opts...); int optional_arg1 = options.get(_optional_arg1, 1); // return options.optional_arg1 or 1 if not set. - int optional_arg2 = options.get(_optional_arg2, 1); - int optional_arg3 = options.get(_optional_arg3, 1); + int optional_arg2 = options.get(_optional_arg2, 12); + int optional_arg3 = options.get(_optional_arg3, 32); } fun(1, _optional_arg3 = 2); // Set the thirds argument and leave the two others to their default value. @@ -182,7 +209,7 @@ fun(1, _optional_arg3 = 2); // Set the thirds argument and leave the two others ## Foreach for tuple and SIO -While C++11 introduce range based for loops for container such as +While C++11 introduce range based for loops for a container such as std::vector, it does not provide a way to iterate on tuples. ```foreach``` is a powerful primitive for processing tuples as well as SIOs. @@ -231,7 +258,7 @@ int res = apply(tuple, fun); ## C++ Embedded Domain Specific Languages Framework -The IOD library provides a set of tools to ease the embedding of +The IOD library provides a set of tools to ease the embedding of an embedded domain specific languages (EDSL) framework. It includes an abstract syntax tree (AST) with symbols and values as terminals. Here is an example of a simple expression: @@ -241,7 +268,7 @@ auto exp = _a(23) + _b; ``` This code does nothing except computing the AST type and storing the -value 23. ```exp``` has to be evaluate to actually compute +value 23. ```exp``` has to be evaluated to actually compute something. IOD provides three handy primitives to traverse ASTs: exp_map_reduce, exp_transform, exp_tranform_iterate. More documentation will come later. In the meantime, you can check how the diff --git a/example.cc b/example.cc deleted file mode 100644 index 10c5ad2..0000000 --- a/example.cc +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include - -#include -#include "iod_json.hh" - -// Declaration of the attributes used by the inline object definitions. -iod_define_attribute(name) -iod_define_attribute(age) -iod_define_attribute(cars) -iod_define_attribute(model) -iod_define_attribute(cities) -iod_define_attribute(lastname) -iod_define_attribute(inc_age) - -int main() -{ - - // Inline object definition. - auto person = iod( - *name = ("Philippe"), // stared fields are serialized. - *age = 42, - - inc_age = [] (auto& self, int inc) { self.age += inc; }, // Requires C++14. - - *cities = {"Paris", "Toronto", "New York City"}, - *cars = { - iod(*name = ("Renault"), model = std::string("Clio")), - // All elements of an array must have the same type. - iod(*name = ("Mercedes"), model = std::string("Class A")) - } - ); - - // Access to the content of the object. - std::cout << person.name << std::endl; - std::cout << person.cars[1].model << std::endl; - - person(inc_age, 10); // Call the inc_age method with arguments (person, 10). - - // Serialize an object to json. - std::string json = iod_to_json(person); - std::cout << json << std::endl; - - // Load an object from a json string. - std::string json_string = R"json({"name":"John", "age": 12})json"; - auto test = iod(*name = "", *age = int()); - - iod_from_json(test, json_string); - - // Extend and object. (todo) - // auto extended_person = iod_extend(person, iod(lastname_ = "Doe")); -} diff --git a/iod/aos_view.hh b/iod/aos_view.hh new file mode 100644 index 0000000..25102ab --- /dev/null +++ b/iod/aos_view.hh @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +#include "sio_utils.hh" +#include "sio.hh" + +namespace iod +{ + + // Iterator, mainly for range based loops + template + struct aos_view_iterator + : public std::iterator()[0])>> + + { + public: + + typedef aos_view_iterator self_t; + typedef std::remove_reference_t()[0])> value_type; + aos_view_iterator(const aos_view_iterator& it) : idx(it.idx), v(it.v) {} + aos_view_iterator(V& _v, int _idx) + : idx(_idx), + v(&_v) + {} + + aos_view_iterator& operator=(const aos_view_iterator& it) + { + idx = it.idx; + v = it.v; + return *this; + } + + decltype(auto) operator*() { return (*v)[idx]; } + self_t& operator++() { ++idx; return *this; } + + int operator-(const self_t& b) { return idx - b.idx; } + self_t& operator--() { idx--; return *this; } + bool operator>(const self_t& b) { return idx > b.idx; } + bool operator>=(const self_t& b) { return idx >= b.idx; } + bool operator<(const self_t& b) { return idx < b.idx; } + bool operator<=(const self_t& b) { return idx <= b.idx; } + bool operator==(const self_t& b) { return idx == b.idx; } + bool operator!=(const self_t& b) { return idx != b.idx; } + + self_t operator+(int b) + { + self_t tmp(*this); + tmp.idx += b; + return tmp; + } + self_t operator-(int b) + { + self_t tmp(*this); + tmp.idx -= b; + return tmp; + } + + int idx; + V* v; + }; + + + template + struct has_size_method + { + template + static char test(decltype(&C::size)); + template + static int test(...); + static const bool value = sizeof(test(0)) == 1; + }; + + + template + auto bind_first_arg(F f, int a, std::tuple*) + { + return [=] (T... args) -> decltype(auto) + { + return f(a, std::forward(args)...); + }; + } + + // Array of structures view. + template + struct aos_view_ + { + typedef aos_view_ self_t; + typedef aos_view_iterator iterator; + + aos_view_(int size, A&&... a) + : hand_set_size(size), + arrays(( + typename A::left_t::template variable_type(a.right) + )...) + { + //void* x = arrays; + assert(check_sizes() && "All arrays must have the same size."); + } + + aos_view_(A&&... a) + : hand_set_size(-1), + arrays(( + typename A::left_t::template variable_type(a.right) + )...) + { + assert(check_sizes() && "All arrays must have the same size."); + } + + bool check_sizes() + { + // Check if array have the same sizes. + int s = static_cast(size()); + bool res = true; + foreach(arrays) | [&res, s] (auto m) + { + static_if>::value>( + [&res, s] (auto m) { + res &= (int(m.value().size()) == s); + }, + [] (auto) {}, + m); + }; + return res; + } + + // Get the size of the AOS. + size_t size() const { + if (hand_set_size > -1) + return static_cast(hand_set_size); + + int res = -1; + foreach(arrays) | [&res] (auto m) + { + static_if>::value>( + [&res] (auto m) { res = static_cast(m.value().size()); }, + [] (auto) { }, + m); + }; + + assert(res >= 0 && "At least one array with a size method is needed"); + return static_cast(res); + } + + // Access to the ith element. + decltype(auto) operator[](int i) const + { + return D_as_reference((const_cast(this)->template access_member(i))...); + } + decltype(auto) operator[](int i) + { + return D_as_reference((this->template access_member(i))...); + } + + // iterators for range based for loops. + auto begin() { return iterator(*this, 0); } + auto end() { return iterator(*this, static_cast(size())); } + + private: + // Helper to access the ith element, whether the set + // is computed or in memory. + template + decltype(auto) access_member(int i) + { + typedef decltype(S().member_access(arrays)) array_type; + + //return S() = ref(S().member_access(arrays)[i]); + + return S() = + (static_if::value>( + // If m is a function + [i] (auto&& m) -> decltype(auto) { + typedef std::remove_reference_t F; + return static_if<(callable_traits::arity == 1)>( + // If only one int arg: + [i] (auto m) { return m(i); }, + // If more than one arg: + [i] (auto m) { return bind_first_arg(m, i, (callable_arguments_tuple_t*)0); }, + m); + }, + [i] (auto&& m) -> decltype(auto) { return m[static_cast(i)];}, + S().member_access(arrays))); + } + + int hand_set_size; + sio...> arrays; + }; + + // Builder + template + auto aos_view(A&&... a) + { + return aos_view_(std::forward(a)...); + } + + template + auto aos_view(int size, A&&... a) + { + return aos_view_(size, std::forward(a)...); + } + + // Sort elements of a view. + // Only impact in memory arrays. + template + void sort(aos_view_& v, const C& comp) + { + //return 1; + } +} + +// sort(view) +// view.push_back(_a = 12, _b = 23); + diff --git a/iod/apply.hh b/iod/apply.hh index 6fb0403..e71f633 100644 --- a/iod/apply.hh +++ b/iod/apply.hh @@ -64,10 +64,10 @@ namespace iod // tuples. template static decltype(auto) run(std::tuple& o, T&&... t) - { return run_tuple::run(o, std::forward(t)...); } + { return run_tuple(sizeof...(M))>::run(o, std::forward(t)...); } template static decltype(auto) run(const std::tuple& o, T&&... t) - { return run_tuple::run(o, std::forward(t)...); } + { return run_tuple(sizeof...(M))>::run(o, std::forward(t)...); } // Other types template @@ -111,7 +111,7 @@ namespace iod template decltype(auto) apply(T&&... t) { - return run_apply::run(std::forward(t)...); + return run_apply(sizeof...(T) - 1)>::run(std::forward(t)...); } diff --git a/iod/array_view.hh b/iod/array_view.hh new file mode 100644 index 0000000..c4e3c32 --- /dev/null +++ b/iod/array_view.hh @@ -0,0 +1,45 @@ +#pragma once + +#include "sio_utils.hh" +#include "sio.hh" +#include "symbols.hh" +#include "aos_view.hh" + +namespace iod +{ + + template + struct array_view_ + { + typedef array_view_ self_t; + + array_view_(V _view) : view(_view) {} + + decltype(auto) operator[](int i) const + { + return view[i].elt; + } + + decltype(auto) operator[](int i) + { + return view[i].elt; + } + + auto size() const { return view.size(); } + auto begin() { return aos_view_iterator(*this, 0); } + auto end() { return aos_view_iterator(*this, static_cast(view.size())); } + + V view; + }; + + // Builder + template + auto array_view(int size, A&& a) + { + typedef std::remove_reference_t(a))> var_type; + typedef aos_view_ aos_type; + + return array_view_(aos_type(size, s::_elt = std::forward(a))); + } + +} diff --git a/iod/bind_method.hh b/iod/bind_method.hh index d4c5fd5..c86d57b 100644 --- a/iod/bind_method.hh +++ b/iod/bind_method.hh @@ -8,5 +8,11 @@ namespace iod { return [&o, m] (ARGS... a) { return (o.*m)(std::forward(a)...); }; } + + template + auto bind_method(O& o, R (O::*m)(ARGS...) const) + { + return [&o, m] (ARGS... a) { return (o.*m)(std::forward(a)...); }; + } } diff --git a/iod/callable_traits.hh b/iod/callable_traits.hh index 06608be..acba1da 100644 --- a/iod/callable_traits.hh +++ b/iod/callable_traits.hh @@ -1,5 +1,7 @@ #pragma once +#include "typelist.hh" + namespace iod { namespace internal @@ -28,6 +30,7 @@ namespace iod typedef std::false_type is_callable; static const int arity = 0; typedef std::tuple<> arguments_tuple; + typedef typelist<> arguments_list; typedef void return_type; }; @@ -45,6 +48,7 @@ namespace iod typedef std::true_type is_callable; static const int arity = super::arity; typedef typename super::arguments_tuple arguments_tuple; + typedef typename super::arguments_list arguments_list; typedef typename super::return_type return_type; }; @@ -54,6 +58,7 @@ namespace iod typedef std::true_type is_callable; static const int arity = sizeof...(ARGS); typedef std::tuple arguments_tuple; + typedef typelist arguments_list; typedef R return_type; }; @@ -63,6 +68,7 @@ namespace iod typedef std::true_type is_callable; static const int arity = sizeof...(ARGS); typedef std::tuple arguments_tuple; + typedef typelist arguments_list; typedef R return_type; }; @@ -72,6 +78,7 @@ namespace iod typedef std::true_type is_callable; static const int arity = sizeof...(ARGS); typedef std::tuple arguments_tuple; + typedef typelist arguments_list; typedef R return_type; }; @@ -82,12 +89,15 @@ namespace iod typedef std::true_type is_callable; static const int arity = sizeof...(ARGS); typedef std::tuple arguments_tuple; + typedef typelist arguments_list; typedef R return_type; }; template using callable_arguments_tuple_t = typename callable_traits::arguments_tuple; template + using callable_arguments_list_t = typename callable_traits::arguments_list; + template using callable_return_type_t = typename callable_traits::return_type; template diff --git a/iod/di.hh b/iod/di.hh index 29d1778..f821585 100644 --- a/iod/di.hh +++ b/iod/di.hh @@ -5,6 +5,7 @@ #include #include #include +#include namespace iod { @@ -12,63 +13,16 @@ namespace iod namespace di { - template - struct remove_rvalue_reference { typedef T type; }; - template - struct remove_rvalue_reference { typedef T type; }; - - template - using remove_rvalue_reference_t = typename remove_rvalue_reference::type; - - template - struct tuple_remove_rvalues; - template - struct tuple_remove_rvalues > - { - typedef std::tuple::type...> type; - }; - - template - using tuple_remove_rvalues_t = typename tuple_remove_rvalues::type; - - template - decltype(auto) forward_as_tuple_no_rvalue(T&&... t) - { - return std::tuple...>(std::forward(t)...); - } - - template - struct tuple_filter_references - { - typedef T type; - }; - - template - struct tuple_filter_references> - { - typedef typename tuple_filter_references>::type type; - }; - - template - struct tuple_filter_references > - { - typedef decltype(std::tuple_cat(std::declval>(), - std::declval>::type>())) type; - }; - - template - using tuple_filter_references_t = typename tuple_filter_references::type; - template - auto call_factory_instantiate(M& factory, C&& ctx, std::tuple*) + decltype(auto) call_factory_instantiate(M& factory, C&& ctx, typelist*) { - return factory.instantiate(tuple_get_by_type(ctx)...); + return factory.instantiate(std::forward(tuple_get_by_type(ctx))...); } template - auto call_factory_instantiate_static(C&& ctx, std::tuple*) + decltype(auto) call_factory_instantiate_static(C&& ctx, typelist*) { - return std::decay_t::instantiate(tuple_get_by_type(ctx)...); + return std::decay_t::instantiate(std::forward(tuple_get_by_type(ctx))...); } // dependencies_of allows to forward dependencies of a function to another. @@ -79,10 +33,10 @@ namespace iod { return dependencies_of_(); } - + std::tuple<> deps; }; - + template struct dependencies_of_> { @@ -95,50 +49,10 @@ namespace iod std::tuple...> deps; }; - + template using dependencies_of = dependencies_of_>; - // tuple_iterate provides an iteration on a tuple with knowledge of the - // return value of the previous loop step. - template - inline - auto - tuple_iterate_loop(std::enable_if_t*, F, A&&, P&& prev) - { - return prev; - } - - template - inline - auto - tuple_iterate_loop(std::enable_if_t*, F f, A&& t, P&& prev) - { - return tuple_iterate_loop(0, f, t, f(std::get(t), prev)); - } - - template - struct tuple_iterate_caller - { - tuple_iterate_caller(T&& t, P&& prev_init) : t_(t), prev_init_(prev_init) {} - - template - auto operator|(F f) - { - const int size = std::tuple_size>::value; - return tuple_iterate_loop<0, size>(0, f, t_, prev_init_); - } - - const T t_; - P prev_init_; - }; - - template - auto tuple_iterate(T&& t, C&& init) - { - return tuple_iterate_caller(t, init); - } - template struct has_instantiate_static_method { @@ -162,89 +76,84 @@ namespace iod typedef not_found type; }; + template + using di_factory_return_t = std::remove_reference_t::instantiate)>>; + template struct find_di_factory_iterator, I, - std::enable_if_t::instantiate)> >, - I>::value> > + std::enable_if_t, I>::value> > : public find_di_factory_iterator, I> {}; - + template struct find_di_factory_iterator, I, - std::enable_if_t::instantiate)> >, - I>::value> > + std::enable_if_t, I>::value> > { typedef T type; }; + template + struct find_di_factory_iterator, I, + std::enable_if_t>::value> > + : public find_di_factory_iterator, I> + {}; + template - using find_di_factory_t = std::remove_reference_t::type>; - + using find_di_factory_t = std::remove_reference_t>::type>; + // Provide an element of type E: // If \to_inject contains an element of type E, return it. // If \to_inject contains an element f and f.instantiate return type is E, // return f.instantiate(...). Note: \to_inject must contains the arguments of f::instantiate. // Otherwise, call the static method E::instantiate. - struct X {}; template decltype(auto) di_meta_instantiate(T&& to_inject, F f) { - typedef std::remove_reference_t T2; + typedef std::decay_t T2; typedef std::remove_const_t> E2; - //dummy_t x = tuple_get_by_type(to_inject); - //std::string& x = (E*)0; - //void* x= std::string(); - //void* x = to_inject; - return static_if::value or - tuple_embeds::value or - tuple_embeds::value - >( - [&] (auto& to_inject) -> decltype(auto) { - // If to_inject already embeds an element of type E, return it. - auto instantiate = [&] (auto) -> decltype(auto) { - return tuple_get_by_type(to_inject); }; - return f(instantiate, (std::tuple<>*)0); - }, - [&] (auto& to_inject) -> decltype(auto) { - - typedef find_di_factory_t FT; - - return iod::static_if::value>( - // If to_inject embed a factory, call it. - [&] (auto&& deps, auto* e, auto* ft_) -> auto { - typedef std::remove_pointer_t FT_; - typedef iod::callable_arguments_tuple_t ARGS; - auto instantiate = [&] (auto&& ctx) - { - return call_factory_instantiate(tuple_get_by_type(deps), ctx, (ARGS*)0); - }; - return f(instantiate, (ARGS*)0); - }, - // If the argument type provide a static instantiate method, call it. - [&] (auto&& deps, auto* e, auto* ft_) -> auto { - typedef std::remove_pointer_t> E2; - static_assert(has_instantiate_static_method::value, - "Dependency injection failed. Cannot resolve."); - typedef iod::callable_arguments_tuple_t ARGS; - auto instantiate = [&] (auto& ctx) { return call_factory_instantiate_static>(ctx, (ARGS*)0); }; - return f(instantiate, (ARGS*)0); - }, - to_inject, (E2*)0, (FT*)0); - }, - to_inject); + typedef find_di_factory_t FT; + return iod::static_if::value>( + // If to_inject embed a factory, call it. + [f] (auto&& /*deps*/, auto* /*e*/, auto* ft_) -> decltype(auto) { + typedef std::remove_pointer_t FT_; + typedef iod::callable_arguments_list_t ARGS; + auto instantiate = [&] (auto&& ctx) -> decltype(auto) + { + return call_factory_instantiate(tuple_get_by_type(ctx), ctx, (ARGS*)0); + }; + return f(instantiate, (ARGS*)0); + }, + // If the argument type provide a static instantiate method, call it. + [f] (auto&& /*deps*/, auto* e, auto* /*ft_*/) -> decltype(auto) { + typedef std::remove_pointer_t> E2; + static_assert(has_instantiate_static_method::value, + "Dependency injection failed. Cannot resolve."); + typedef iod::callable_arguments_list_t ARGS; + auto instantiate = [&] (auto&& ctx) -> decltype(auto) { return call_factory_instantiate_static>(ctx, (ARGS*)0); }; + return f(instantiate, (ARGS*)0); + }, + to_inject, (E2*)0, (FT*)0); + } + + template + decltype(auto) instantiate(std::enable_if_t, E>::value>*, + T&&... to_inject) + { + return di_meta_instantiate(std::forward_as_tuple(to_inject...), + [&] (auto instantiate, auto* /*args*/) -> decltype(auto) + { + return instantiate(std::forward_as_tuple(to_inject...)); + }); } - template - decltype(auto) instantiate(T&& to_inject) + template + decltype(auto) instantiate(std::enable_if_t, E>::value>*, + T&&... to_inject) { - return di_meta_instantiate(to_inject, - [&] (auto instantiate, auto* args) -> decltype(auto) - { - return instantiate(to_inject); - }); - } + return arg_get_by_type(std::forward(to_inject)...); + } template decltype(auto) create_di_ctx_rec(T&& ctx); @@ -252,7 +161,7 @@ namespace iod template struct create_di_ctx_iterator {}; - + template <> struct create_di_ctx_iterator<> { @@ -262,154 +171,77 @@ namespace iod return std::forward

(prev); } }; - - template + + template struct create_di_ctx_iterator { template static decltype(auto) run(P&& prev) { - typedef std::remove_reference_t(prev))> D; - D& d = *(D*)0; - return create_di_ctx_iterator::template run(d); + return create_di_ctx_iterator::template run(create_di_ctx_rec(prev)); } }; + // Instantiate the injection list from the types A... // returns the concatenation of ctx, elements of type A... and // the elements required to build them. template - decltype(auto) create_di_ctx_list_rec(C&& ctx, std::tuple*) + decltype(auto) create_di_ctx_list_rec(C&& ctx, typelist*) { return create_di_ctx_iterator::template run(ctx); } - + template decltype(auto) create_di_ctx_rec(T&& ctx) { - return di_meta_instantiate(ctx, - [&] (auto instantiate, auto args) -> decltype(auto) - { - auto& ctx2 = ctx; // Fix to import ctx in the closure with gcc 4.9. - typedef std::remove_pointer_t ARGS; - typedef - std::remove_reference_t D; - D& deps = *(D*)0; - return std::tuple_cat(deps, forward_as_tuple_no_rvalue(instantiate(deps))); - }); + return static_if, E>::value>( + [] (auto&& ctx) { return ctx; }, + [] (auto&& ctx) { + return di_meta_instantiate(*(typelist_to_tuple_t>*)42, + [&] (auto /*instantiate*/, auto args) -> decltype(auto) + { + typedef std::remove_pointer_t ARGS; + auto deps = create_di_ctx_list_rec(ctx, (ARGS*)0); + return typelist_cat(deps, typelist()); + }); + }, ctx); } - template - decltype(auto) create_di_ctx(std::enable_if_t*, - std::tuple*, U&&... args) + template + decltype(auto) create_stack_and_call(typelist<>*, + typelist*, + F fun, B&&... to_inject) { - return std::tuple(std::forward(args)...); - }; - - template - decltype(auto) create_di_ctx(std::enable_if_t<(sizeof...(T) > sizeof...(U))>*, - std::tuple* ctx, U&&... args) - { - return create_di_ctx(0, ctx, std::forward(args)..., - instantiate>> - (std::forward_as_tuple(args...))); - }; - - template - struct static_array - { - unsigned char data[N]; - }; - - template - decltype(auto) create_di_ctx2(std::enable_if_t<(N == sizeof...(T))>*, - std::tuple& ctx, U&&... args) - { - }; - - - template - auto sub_tuple(std::enable_if_t<(N == 0)>*, - std::tuple&& t, T&...) - { - return t; + return fun(arg_get_by_type(std::forward(to_inject)...)...); } - template - auto sub_tuple(std::enable_if_t<(N > 0)>*, - std::tuple&& t, T1& t1, T&... args) + template + decltype(auto) create_stack_and_call(typelist*, + typelist* args, + F fun, B&&... to_inject) { - return sub_tuple(0, std::tuple_cat(t, std::forward_as_tuple(t1)), args...); - } - - template - decltype(auto) create_di_ctx2(std::enable_if_t<(N < sizeof...(T))>*, - std::tuple& ctx, U&&... args) - { - typedef std::remove_reference_t(ctx))> X; - new (&std::get(ctx)) X(instantiate>> - (std::tuple_cat(std::forward_as_tuple(args...), - //std::forward_as_tuple(tuple_get_by_type(ctx)...) - sub_tuple(0, std::make_tuple(), tuple_get_by_type(ctx)...) - ))); - create_di_ctx2(0, ctx, std::forward(args)...); - }; - - template - decltype(auto) get_from_ctx_or_args(std::tuple& ctx, B&&... to_inject) - { - return tuple_get_by_type( - std::tuple_cat(std::forward_as_tuple(tuple_get_by_type(ctx)...), - std::forward_as_tuple(to_inject...)) - ); - } - - template - struct desctructor_caller - { - desctructor_caller(F f) : f_(f) {} - ~desctructor_caller() { f_(); } - F f_; - }; - - template - auto make_destructor_caller(F f) - { - return desctructor_caller(f); + return create_stack_and_call((typelist*)0, args, fun, + std::forward(to_inject)..., + instantiate(0, std::forward(to_inject)...)); } // Call fun with its required argument A... // by picking in args... or calling A::instantiate() template - auto call_with_di2(F fun, std::tuple*, B&&... to_inject) + decltype(auto) + call_with_di2(F fun, std::tuple* /*arguments*/, B&&... to_inject) { - - // function arguments type. - callable_arguments_tuple_t* arguments; - // Compute the context type containing the arguments plus the // dependencies of the possible A::instantiate(...) methods. typedef std::remove_reference_t< decltype(create_di_ctx_list_rec( - std::declval...>>(), - arguments)) + std::declval>(), + (typelist*)0)) > ctx_type; - // Remove the rvalues references to be able to store the context. - typedef tuple_remove_rvalues_t ctx_type2; - typedef tuple_filter_references_t ctx_type3; - // Instantiate it. - static_array ctx_buffer; - ctx_type3& ctx = *(ctx_type3*) &ctx_buffer; - - // Destroy it at the end of the scope. - auto x = make_destructor_caller([&] () { ctx.~ctx_type3(); }); - - // Create the needed dependencies: - create_di_ctx2<0>(0, ctx, std::forward(to_inject)...); - - // Call the function. - return fun(get_from_ctx_or_args(ctx, std::forward(to_inject)...)...); + // typedef typename tuple_to_list::type ctx_typelist; + return create_stack_and_call((ctx_type*)0, (typelist*)0, fun, to_inject...); } } @@ -443,5 +275,4 @@ namespace iod return di::call_with_di2(bind_method(o, fun), (callable_arguments_tuple_t*)0, std::forward(to_inject)...); } } di_call_method; - } diff --git a/iod/foreach.hh b/iod/foreach.hh index 04cb75c..8a7dd74 100644 --- a/iod/foreach.hh +++ b/iod/foreach.hh @@ -16,47 +16,61 @@ namespace iod namespace internal { - template - inline - decltype(auto) - foreach_loop_tuple(std::enable_if_t*, F, A&&, R&&... results) + template + decltype(auto) tuple_foreach_i(F f, T&&... ts) { - return static_if( - [] () {}, - [&] () { return std::make_tuple(results...);}); + return f(std::get(ts)...); } - template - inline - decltype(auto) - foreach_loop_tuple(std::enable_if_t*, F f, A&& args_tuple, R&&... results) + template + inline void + tuple_foreach_impl(std::enable_if_t::value>*, + std::index_sequence, + F f, T&&... ts) { - auto h = [] (auto&& a) -> decltype(auto) - { - return std::forward(a))>(std::get(a)); - }; - typedef decltype(h) H; - typedef decltype(proxy_apply(args_tuple, std::declval(), f)) return_type; - - return static_if::value>( - [&] (auto& args_tuple, auto& h, auto& f) - { - proxy_apply(args_tuple, h, f); - return foreach_loop_tuple(0, f, args_tuple, std::forward(results)...); - }, - [&] (auto& args_tuple, auto& h, auto& f) -> decltype(auto) - { - return foreach_loop_tuple - (0, f, args_tuple, std::forward(results)..., proxy_apply(args_tuple, h, f)); - }, args_tuple, h, f); + return (void)std::initializer_list{ + ((void)tuple_foreach_i(f, std::forward(ts)...), 0)...}; + } + + template + inline decltype(auto) + tuple_foreach_impl(std::enable_if_t::value>*, + std::index_sequence, + F f, T&&... ts) + { + return std::make_tuple(iod::internal::tuple_foreach_i(f, std::forward(ts)...)...); + } + + template + decltype(auto) sio_foreach_i(F f, T&&... ts) + { + return f(ts.template get_nth_member()...); } + template + inline void + sio_foreach_impl(std::enable_if_t::value>*, + std::index_sequence, + F f, T&&... ts) + { + return (void)std::initializer_list{ + ((void)sio_foreach_i(f, std::forward(ts)...), 0)...}; + } + template + inline decltype(auto) + sio_foreach_impl(std::enable_if_t::value>*, + std::index_sequence /*si*/, + F f, T&&... ts) + { + return D(iod::internal::sio_foreach_i(f, std::forward(ts)...)...); + } + template inline auto - foreach_loop_sio(std::enable_if_t*, F, A&&, R&&... results) + foreach2_loop_sio(std::enable_if_t*, F, A&&, R&&... results) { return static_if( [] () {}, @@ -66,11 +80,11 @@ namespace iod template inline auto - foreach_loop_sio(std::enable_if_t*, F f, A&& args_tuple, R&&... results) + foreach2_loop_sio(std::enable_if_t*, F f, A&& args_tuple, R&&... results) { auto h = [] (auto&& a) -> auto&& // avoid the lambda to convert references to values. { - return std::forward())>(a.template get_nth_attribute()); + return std::forward())>(a.template get_nth_member()); }; typedef decltype(h) H; typedef decltype(proxy_apply(args_tuple, std::declval(), f)) return_type; @@ -79,47 +93,105 @@ namespace iod [&] (auto& args_tuple) { proxy_apply(args_tuple, h, f); - return foreach_loop_sio(0, f, args_tuple, results...); + return foreach2_loop_sio(0, f, args_tuple, results...); }, [&] (auto& args_tuple) { - return foreach_loop_sio + return foreach2_loop_sio (0, f, args_tuple, results..., proxy_apply(args_tuple, h, f)); }, args_tuple); } + + template + inline void tuple_foreach(F, std::tuple<>, T&&...) + { + } + + template + inline decltype(auto) tuple_foreach(F f, T1&& t1 , T&&... ts) + { + using seq = std::make_index_sequence>::value>; + using Ret = decltype(f(std::get<0>(t1), std::get<0>(ts)...)); + return iod::internal::tuple_foreach_impl(0, seq{}, + f, t1, std::forward(ts)...); + } + + template + inline void sio_foreach(std::enable_if_t::_size == 0>*, + F /*f*/, T1&& /*t1*/, T&&... /*ts*/) + { + } + + template + inline decltype(auto) sio_foreach(std::enable_if_t::_size != 0>*, + F f, T1&& t1 , T&&... ts) + { + const size_t size = std::remove_reference_t::_size; + using seq = std::make_index_sequence; + using Ret = decltype(f(t1.template get_nth_member<0>(), ts.template get_nth_member<0>()...)); + return iod::internal::sio_foreach_impl(0, seq{}, + f, t1, std::forward(ts)...); + } template struct foreach_tuple_caller { foreach_tuple_caller(T&& t) : t_(t) {} + template + decltype(auto) run(F f, std::index_sequence) + { + return iod::internal::tuple_foreach(f, std::get(t_)...); + } + template - auto operator|(F f) + decltype(auto) operator|(F f) { - const int size = std::tuple_size(t_))>>::value; - return internal::foreach_loop_tuple<0, size>(0, f, t_); + return run(f, std::make_index_sequence::value>{}); } const T t_; }; + template struct foreach_sio_caller { foreach_sio_caller(T&& t) : t_(t) {} + + template + decltype(auto) run(F f, std::index_sequence) + { + return iod::internal::sio_foreach(0, f, std::get(t_)...); + } + + template + decltype(auto) operator|(F f) + { + return run(f, std::make_index_sequence::value>{}); + } + + const T t_; + }; + + template + struct foreach2_sio_caller + { + foreach2_sio_caller(T&& t) : t_(t) {} + template auto operator|(F f) { const int size = std::remove_reference_t(t_))>::_size; - return internal::foreach_loop_sio<0, size>(0, f, t_); + return internal::foreach2_loop_sio<0, size>(0, f, t_); } const T t_; }; - + } template @@ -151,6 +223,21 @@ namespace iod } + template + auto foreach2(sio& a1, T&&... args) + { + return internal::foreach2_sio_caller + (std::forward_as_tuple(a1, args...)); + } + + template + auto foreach2(const sio& a1, T&&... args) + { + return internal::foreach2_sio_caller + (std::forward_as_tuple(a1, args...)); + } + + namespace internal { @@ -231,16 +318,43 @@ namespace iod (std::forward_as_tuple(a1, args...), prev_init); } - // template - // auto foreach(F f, sio& a1, T&&... args) - // { - // return internal::foreach_loop_sio<0, sizeof...(S)>(0, f, std::forward_as_tuple(a1, args...)); - // } - - // template - // auto foreach(F f, const sio& a1, T&&... args) - // { - // return internal::foreach_loop_sio<0, sizeof...(S)>(0, f, std::forward_as_tuple(a1, args...)); - // } + template + inline + auto + sio_iterate_loop(std::enable_if_t*, F f, const O& o, const P& prev) + { + auto new_prev = f(o.template get_nth_member(), prev); + return sio_iterate_loop(0, f, o, new_prev); + } + + template + inline + auto + sio_iterate_loop(std::enable_if_t*, F /*f*/, const O& /*o*/, const P& prev) + { + return prev; + } + + template + struct sio_iterate_caller + { + sio_iterate_caller(O o, P init) : o_(o), init_(init) {} + + template + auto operator|(F f) + { + const int size = O::size(); + return sio_iterate_loop<0, size>(0, f, o_, init_); + } + + const O o_; + P init_; + }; + + template + auto sio_iterate(O o, I init) + { + return sio_iterate_caller(o, init); + } } diff --git a/iod/grammar.hh b/iod/grammar.hh index 808be31..56aa274 100644 --- a/iod/grammar.hh +++ b/iod/grammar.hh @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -10,12 +11,6 @@ namespace iod template struct grammar_value_type { typedef T type; }; - template <> - struct grammar_value_type { typedef std::string type; }; - template - struct grammar_value_type { typedef std::string type; }; - template - struct grammar_value_type { typedef std::string type; }; template struct grammar_value_type > { typedef std::vector::type> type; }; @@ -50,7 +45,7 @@ namespace iod struct symbol; template - struct member_accessible; + struct array_subscriptable; template struct callable; @@ -58,6 +53,15 @@ namespace iod template struct assignable; + template + struct array_subscriptable; + + // template + // struct dividable; + + // template + // struct multipliable; + template auto foreach_prev(std::tuple& a1, T&&... args); template @@ -82,7 +86,7 @@ namespace iod // Terminals. template - decltype(auto) exp_transform_iterate(E& exp, F map, + decltype(auto) exp_transform_iterate(E& exp, F /*map*/, C ctx, std::enable_if_t::value and !has_transform_iterate::value>* = 0) @@ -115,7 +119,7 @@ namespace iod // Terminals. template - decltype(auto) exp_transform(E& exp, F map, C& ctx, + decltype(auto) exp_transform(E& exp, F /*map*/, C& /*ctx*/, std::enable_if_t::value and !has_transform_iterate::value>* = 0) { @@ -145,8 +149,8 @@ namespace iod // Terminals. template - decltype(auto) exp_map_reduce(E& exp, N neutral, C& ctx, - M map, R reduce, + decltype(auto) exp_map_reduce(E& /*exp*/, N neutral, C& /*ctx*/, + M /*map*/, R /*reduce*/, std::enable_if_t::value and !has_transform_iterate::value>* = 0) { @@ -154,8 +158,8 @@ namespace iod } template - decltype(auto) exp_map_reduce(E& exp, N neutral, C& ctx, - M map, R reduce, + decltype(auto) exp_map_reduce(E& exp, N /*neutral*/, C& ctx, + M map, R /*reduce*/, std::enable_if_t::value>* = 0) { return map(exp, ctx); @@ -182,7 +186,7 @@ namespace iod // Terminals. template - inline decltype(auto) exp_evaluate(E& exp, M eval, C& ctx, + inline decltype(auto) exp_evaluate(E& exp, M /*eval*/, C& /*ctx*/, std::enable_if_t::value and !has_transform_iterate::value>* = 0) { @@ -223,17 +227,17 @@ namespace iod template struct function_call_exp : - public member_accessible>, + public array_subscriptable>, public callable>, public assignable>, public Exp> { using assignable>::operator=; - using member_accessible>::operator[]; + using array_subscriptable>::operator[]; function_call_exp() {} function_call_exp(const M& m, A&&... a) - : method(m), args(a...) {} + : method(m), args(std::forward(a)...) {} function_call_exp(const M& m, std::tuple& a) : method(m), args(a) {} function_call_exp(const M& m, const std::tuple& a) @@ -269,16 +273,16 @@ namespace iod }; template - struct member_accessor_exp : - public member_accessible>, - public callable>, - public assignable>, - public Exp> + struct array_subscript_exp : + public array_subscriptable>, + public callable>, + public assignable>, + public Exp> { - using assignable>::operator=; + using assignable>::operator=; - member_accessor_exp() {} - member_accessor_exp(const O& o, const M& m) : object(o), member(m) {} + array_subscript_exp() {} + array_subscript_exp(const O& o, const M& m) : object(o), member(m) {} template auto visit(F f) { return std::make_tuple(f(object), f(member)); } @@ -286,7 +290,7 @@ namespace iod auto transform(F f, C ctx) { auto o = exp_transform(object, f, ctx); auto m = exp_transform(member, f, ctx); - return member_accessor_exp(o, m); + return array_subscript_exp(o, m); } template @@ -294,7 +298,7 @@ namespace iod { auto l = exp_transform_iterate(object, f, ctx); auto r = exp_transform_iterate(member, f, l.second); - return std::make_pair(member_accessor_exp + return std::make_pair(array_subscript_exp (l.first, r.first), r.second); } @@ -312,9 +316,11 @@ namespace iod template struct assign_exp : public Exp> { - //assign_exp() {} + typedef L left_t; + typedef R right_t; + assign_exp(L&& l, R&& r) : left(l), right(r) {} - assign_exp(const L& l, const R& r) : left(l), right(r) {} + assign_exp(const L& l, R&& r) : left(l), right(std::forward(r)) {} template auto visit(F f) { return std::make_tuple(f(left), f(right)); } @@ -342,16 +348,16 @@ namespace iod L left; R right; }; - + template - struct member_accessible + struct array_subscriptable { public: // Member accessor template constexpr auto operator[](const S& s) const { - return member_accessor_exp(*static_cast(this), s); + return array_subscript_exp(*static_cast(this), s); } }; @@ -369,42 +375,39 @@ namespace iod } }; - + template struct assignable { public: template - auto operator=(const L& l) const + auto operator=(L&& l) const { - return assign_exp>(*static_cast(this), grammar_value_type_t(l)); + return assign_exp(static_cast(*this), std::forward(l)); } - template - inline auto operator=(const std::initializer_list& l) const + template + auto operator=(L&& l) { - return assign_exp>> - (*static_cast(this), grammar_value_type_t>(l)); + return assign_exp(static_cast(*this), std::forward(l)); } - - // Special case for initializer_list that cannot implicitely build a vector. - inline auto operator=(const std::initializer_list& l) const + + template + auto operator=(const std::initializer_list& l) const { - std::vector v; - for (auto s : l) v.push_back(s); - - return assign_exp> - (*static_cast(this), v); + return assign_exp>(static_cast(*this), std::vector(l)); } }; #define iod_query_declare_binary_op(OP, NAME) \ template \ - struct NAME##_exp : public Exp> \ + struct NAME##_exp : \ + public assignable>, \ + public Exp> \ { \ + using assignable>::operator=; \ NAME##_exp() {} \ NAME##_exp(A a, B b) : lhs(a), rhs(b) {} \ typedef A lhs_type; \ diff --git a/iod/json.hh b/iod/json.hh index 1679999..2d7f9ac 100644 --- a/iod/json.hh +++ b/iod/json.hh @@ -7,11 +7,12 @@ #include #include #include -#include #include #include #include +#include +#include #include #include #include @@ -33,13 +34,158 @@ namespace iod template inline std::string json_encode(const sio& o); + // Encode \o into a json string. + template + inline std::string json_encode(const std::vector& v); + // Encode \o into a stream. template inline void json_encode(const sio& o, S& stream); + + + struct json_string { std::string str; }; + inline std::string json_encode(const json_string& o); + namespace json_internals { + struct external_char_stream + { + + external_char_stream(char* buf, int len) + : pos_(0), + buf_(buf), + max_len_(len) + {} + + inline void append(const char t) + { + if (pos_ == max_len_) + throw std::runtime_error("Maximum json string lenght reached during encoding."); + buf_[pos_] = t; + pos_++; + } + + inline void append(const stringview s) + { + if (pos_ + s.size() > max_len_) + throw std::runtime_error("Maximum json string lenght reached during encoding."); + memcpy(buf_ + pos_, s.data(), static_cast(s.size())); + pos_ += s.size(); + } + + int size() { return pos_; } + + int pos_; + char* buf_; + int max_len_; + }; + + static const int LBS = 500; + struct stringstream + { + + stringstream(int hint_size = 10) + : pos_(0) + { str_.reserve(static_cast(hint_size)); } + + inline void append(const char t) + { + if (pos_ == LBS) + flush(); + buf_[pos_] = t; + pos_++; + } + + inline void append(const stringview s) + { + const char* begin = s.data(); + const char* end = s.data() + s.size(); + + while (int(end - begin) > (LBS - pos_)) + { + flush(); + int to_write = std::min(int(end - begin), LBS); + + memcpy(buf_, begin, static_cast(to_write)); + begin += to_write; + pos_ += to_write; + } + + memcpy(buf_ + pos_, begin, static_cast(end - begin)); + + pos_ += static_cast(end - begin); + } + + inline void flush() + { + str_.resize(str_.size() + static_cast(pos_)); + memcpy(&(str_)[0] + str_.size() - pos_, buf_, static_cast(pos_)); + pos_ = 0; + } + + const std::string& str() { + if (pos_ > 0) + flush(); + return str_; + } + + std::string move_str() { + if (pos_ > 0) + flush(); + return std::move(str_); + } + + int pos_; + char buf_[LBS]; + std::string str_; + }; + + + template + struct my_ostringstream : public S + { + using S::S; + + inline my_ostringstream& operator<<(const char t) { + S::append(t); + return *this; + } + inline my_ostringstream& operator<<(const stringview& t) { + S::append(t); + return *this; + } + inline my_ostringstream& operator<<(const std::string& t) { + S::append(t); + return *this; + } + + inline my_ostringstream& operator<<(const json_string& t) { + (*this) << t.str; + return *this; + } + + // inline my_ostringstream& operator<<(const boost::string_ref& t) { + // (*this) << stringview(&t[0], t.size()); + // return *this; + // } + + template + my_ostringstream& operator<<(const T& t) { + std::string s = boost::lexical_cast(t); + (*this) << stringview(s.c_str(), s.size()); + return *this; + } + + inline my_ostringstream& operator<<(int t) { + std::string s = std::to_string(t); + S::append(stringview(s.c_str(), s.size())); + return *this; + } + + }; + // Json encoder. // ============================================= template @@ -51,18 +197,35 @@ namespace iod template inline void json_encode_(const char* t, S& ss) { - ss << '"' << t << '"'; + std::string s; + utf8_to_json(t, s); + ss << s; } template - inline void json_encode_(const stringview& s, S& ss) + inline void json_encode_(const stringview& t, S& ss) + { + std::string s; + utf8_to_json(t, s); + ss << s; + } + + template + inline void json_encode_symbol(symbol, SS& ss) { ss << '"'; - for (int i = 0; i < s.len; i++) - ss << s.str[i]; + ss << stringview(S().name(), strlen(S().name())); ss << '"'; } - + + // template + // inline void json_encode_(const boost::string_ref& s, S& ss) + // { + // ss << '"'; + // ss << s; + // ss << '"'; + // } + template inline void json_encode_(const std::string& t, S& ss) { @@ -92,13 +255,13 @@ namespace iod ss << '{'; int i = 0; bool first = true; - foreach(o) | [&] (const auto& m) + foreach(o) | [&] (auto m) { if (!m.attributes().has(_json_skip)) { if (!first) { ss << ','; } - first = false; - json_encode_(m.attributes().get(_json_key, m.symbol()).name(), ss); + first = false; + json_encode_symbol(m.attributes().get(_json_key, m.symbol()), ss); ss << ':'; json_encode_(m.value(), ss); } @@ -112,7 +275,7 @@ namespace iod template struct fill_ { - inline fill_(T& _r) : r(_r) {} + inline fill_(T& _rin) : r(_rin) {} T& r; }; @@ -124,13 +287,14 @@ namespace iod { struct spaces_ {} spaces; - inline json_parser(std::istringstream& _stream) : str(_stream.str()), pos(0) {} - inline json_parser(const std::string& _str) : str(_str.c_str(), _str.size()), pos(0) {} - inline json_parser(const stringview& _str) : str(_str), pos(0) {} + inline json_parser(std::istringstream& _istringstream) : str(_istringstream.str()), pos(0) {} + inline json_parser(const std::string& _sv) : str(_sv.c_str(), _sv.size()), pos(0) {} + inline json_parser(const stringview& _sv) : str(_sv), pos(0) {} - inline char peak() { return str[pos]; } + inline char peek() { return str[pos]; } inline char eof() { return pos == str.size(); } - inline char eat_one() { return pos++; } + inline void eat_one() { pos++; } + inline char get() { return str[pos++]; } template inline void format_error(E&) {} @@ -141,7 +305,7 @@ namespace iod err << a; format_error(err, args...); } - + template inline std::runtime_error json_error(T... message) { @@ -150,143 +314,203 @@ namespace iod int w = 20; int b = pos > w ? pos - w : 0; int e = pos < int(str.size()) - w ? pos + w : int(str.size()) - 1; - std::string near(str.data() + b, str.data() + e); - err << std::endl << "Json parse error near " << near << std::endl; + std::string position(str.data() + b, str.data() + e); + err << std::endl << "Json parse error near " << position << std::endl; err << " "; for (int i = 0; i < pos - b - 1; i++) err << ' '; err << "^^^"<< std::endl; format_error(err, message...); return std::runtime_error(err.str()); } - + inline json_parser& fill(std::string& t) + { + json_to_utf8(*this, t); + return *this; + // int start = pos; + // int end = pos; + // t.clear(); + + // char buffer[128]; + // int buffer_pos = 0; + // auto flush = [&] () { t.append(buffer, buffer_pos); buffer_pos = 0; }; + // auto append_char = [&] (char c) + // { + // if (buffer_pos == sizeof(buffer)) flush(); + + // buffer[buffer_pos] = c; + // buffer_pos++; + // }; + // auto append_str = [&] (const char* str, int len) + // { + // if (buffer_pos + len > int(sizeof(buffer))) flush(); + // if (len < int(sizeof(buffer))) + // { + // memcpy(buffer + buffer_pos, str, len); + // buffer_pos += len; + // } + // else + // { + // flush(); + // t.append(str, len); + // } + // }; + + // while (true) + // { + // while (!eof() and str[end] != '"' and str[end] != '\\') + // end++; + + // if (eof()) throw json_error("Unexpected end of string when parsing a string."); + // append_str(str.data() + start, end - start); + + // std::cout << str[end] << std::endl; + // if (str[end] == '"') break; + + // end++; + // switch (str[end]) + // { + // case '\'': append_char('\''); break; + // case '"': append_char('"'); break; + // case '\\': append_char('\\'); break; + // case '/': append_char('/'); break; + // case 'n': append_char('\n'); break; + // case 'r': append_char('\r'); break; + // case 't': append_char('\t'); break; + // case 'b': append_char('\b'); break; + // case 'f': append_char('\f'); break; + // case 'v': append_char('\v'); break; + // case '0': append_char('\0'); break; + // case 'u': + // while (true) + // { + // if (str.size() < end + 4) + // throw json_error("Unexpected end of string when decoding an utf8 character"); + // end++; + + // auto decode_hex_c = [this] (char c) { + // if (c >= '0' and c <= '9') return c - '0'; + // else return (10 + c - 'A'); + // }; + + // const char* str2 = str.data() + end; + // char x = (decode_hex_c(str2[0]) << 4) + decode_hex_c(str2[1]); + // if (x) append_char(x); + // append_char((decode_hex_c(str2[2]) << 4) + decode_hex_c(str2[3])); + + // end += 4; + + // if (str[end] == '\\' and str[end + 1] == 'u') + // end += 1; + // else break; + // } + // break; + // } + + // start = end; + // } + // flush(); + // pos = end; + // return *this; + } + + inline json_parser& fill(stringview& t) { int start = pos; int end = pos; - t.clear(); - char buffer[128]; - int buffer_pos = 0; - auto flush = [&] () { t.append(buffer, buffer_pos); buffer_pos = 0; }; - auto append_char = [&] (char c) - { - if (buffer_pos == sizeof(buffer)) flush(); - - buffer[buffer_pos] = c; - buffer_pos++; - }; - auto append_str = [&] (const char* str, int len) - { - if (buffer_pos + len > int(sizeof(buffer))) flush(); - memcpy(buffer + buffer_pos, str, len); - buffer_pos += len; - }; - while (true) { - while (!eof() and str[end] != '"' and str[end] != '\\') + while (!eof() and str[end] != '"') end++; - if (eof()) throw json_error("Unexpected end of string when parsing a string."); - append_str(str.data() + start, end - start); + // Count the prev backslashes. + int sb = end - 1; + while (sb >= 0 and str[sb] == '\\') + sb--; - if (str[end] == '"') break; - - end++; - switch (str[end]) - { - case '\'': append_char('\''); break; - case '"': append_char('"'); break; - case '\\': append_char('\\'); break; - case '/': append_char('/'); break; - case 'n': append_char('\n'); break; - case 'r': append_char('\r'); break; - case 't': append_char('\t'); break; - case 'b': append_char('\b'); break; - case 'f': append_char('\f'); break; - case 'v': append_char('\v'); break; - case '0': append_char('\0'); break; - case 'u': - while (true) - { - if (str.size() < end + 4) - throw json_error("Unexpected end of string when decoding an utf8 character"); - end++; - - auto decode_hex_c = [this] (char c) { - if (c >= '0' and c <= '9') return c - '0'; - else return (10 + c - 'A'); - }; - - const char* str2 = str.data() + end; - char x = (decode_hex_c(str2[0]) << 4) + decode_hex_c(str2[1]); - if (x) append_char(x); - append_char((decode_hex_c(str2[2]) << 4) + decode_hex_c(str2[3])); - - end += 4; - - if (str[end] == '\\' and str[end + 1] == 'u') - end += 1; - else break; - } - break; - } - - start = end; + if ((end - sb) % 2) break; + else + end++; } - flush(); + + t.str = str.data() + start; + t.len = static_cast(end - start); pos = end; return *this; } - - inline json_parser& fill(stringview& t) + + template typename std::enable_if::value, json_parser>::type& + fill_int(I& val) { - int start = pos; - int end = pos; + int sign = 1; + if (str[pos] == '-') { sign = -1; eat_one(); } + else if (str[pos] == '+') { eat_one(); } + int end = pos; - while (!eof() and str[end] != '"' and str[end - 1] != '\\') end++; - t.str = str.data() + start; - t.len = end - start; - pos = end; - return *this; + val = 0; + + const char* s = str.data() + pos; + + int fz = 0; + while (s[fz] == '0') { fz++; end++; } + + for (int i = fz; i < N + fz; i++) + { + if (s[i] < '0' or s[i] > '9') break; + val = val * 10 + (s[i] - '0'); + end++; + } + + val *= sign; + + if (end == pos) throw json_error("Could not find the expected number."); + + pos = end; + return *this; } - template - inline json_parser& fill_int(I& val) + template typename std::enable_if::value, json_parser>::type& + fill_int(I& val) { - int sign = 1; - if (std::is_signed::value and str[pos] == '-') { sign = -1; eat_one(); } - else if (str[pos] == '+') { eat_one(); } + if (str[pos] == '+') { eat_one(); } - int end = pos; - - val = 0; + int end = pos; - const char* s = str.data() + pos; + val = 0; - - for (int i = 0; i < N; i++) - { - if (s[i] < '0' or s[i] > '9') break; - val = val * 10 + (s[i] - '0'); - end++; - } - val *= sign; + const char* s = str.data() + pos; - if (end == pos) throw json_error("Could not find the expected number."); - - pos = end; - return *this; + int fz = 0; + while (s[fz] == '0') { fz++; end++; } + + for (int i = fz; i < N + fz; i++) + { + if (s[i] < '0' or s[i] > '9') break; + val = val * 10U + static_cast(s[i] - '0'); + end++; + } + + if (end == pos) throw json_error("Could not find the expected number."); + + pos = end; + + return *this; } - + inline json_parser& fill(float& val) { + int sign = 1; + if (str[pos] == '-') { sign = -1; eat_one(); } + else if (str[pos] == '+') { eat_one(); } + float res = 0; - + int ent = 0; - fill_int(ent); + if (str[pos] != '.') + fill_int(ent); - res = ent; + res = static_cast(ent); if (str[pos] == '.') { @@ -295,7 +519,7 @@ namespace iod int start = pos; fill_int(floating); int end = pos; - res += float(floating) / pow_10(end - start); + res += static_cast(floating / pow_10(end - start)); } if (str[pos] == 'e') @@ -303,26 +527,76 @@ namespace iod eat_one(); int exp = 0; fill_int(exp); - res *= pow_10(exp); + res *= static_cast(pow_10(exp)); } - val = res; + val = static_cast(sign) * res; return *this; } - + inline json_parser& fill(int& val) { return fill_int(val); } inline json_parser& fill(unsigned int& val) { return fill_int(val); } - + template inline json_parser& fill(T& t) { + static_assert(!std::is_same::value, + "Cannot json deserialize into an object with const char* members"); + static_assert(!std::is_same::value, + "Cannot json deserialize into an object with const char[] members"); + int end = pos; - while(!eof() and str[end] != ',' and str[end] != '}' and str[end] != ']') end++; + while(end != str.size() and str[end] != ',' and str[end] != '}' and str[end] != ']') end++; t = boost::lexical_cast>(str.data() + pos, end - pos); pos = end; return *this; } + + // Fill a json_string object with the next json entity. + inline json_parser& operator>>(json_string& t) + { + int start = pos; + int end = pos; + + bool in_str = false; + int parent_level = 0; + int array_level = 0; + bool done = false; + + while (!eof() and !done) + { + if (parent_level == 0 and array_level == 0 and !in_str and + (str[pos] == ',' or str[pos] == '}' or str[pos] == ']' )) + break; + + if (str[pos] == '"') // strings. + { + pos++; + stringview sv; + this->fill(sv); + } + else if (str[pos] == '{' ) // start a json object + parent_level++; + else if (str[pos] == '}' ) // end a json object + parent_level--; + else if (str[pos] == '[' ) // start a json array + array_level++; + else if (str[pos] == ']' ) // end a json array + array_level--; + + pos++; // go to next char. + + // skip spaces + while (!eof() and std::isspace(str[pos])) pos++; + } + + end = pos; + t.str.resize(static_cast(end - start)); + memcpy(static_cast(const_cast((t.str.data()))), static_cast(str.data() + start), static_cast(end - start)); + return *this; + } + template inline json_parser& operator>>(fill_&& t) { @@ -331,7 +605,7 @@ namespace iod inline json_parser& operator>>(char t) { - if (!eof() and str[pos] == t) + if (str[pos] == t) { pos++; return *this; @@ -363,7 +637,7 @@ namespace iod inline json_parser& operator>>(spaces_) { - while (!eof() and std::isspace(str[pos])) pos++; + while (!eof() and str[pos] < 33) pos++; return *this; } @@ -373,26 +647,29 @@ namespace iod int pos; }; - inline void iod_attr_from_json(sio<>&, json_parser&) + template + inline void iod_attr_from_json(S*, sio<>&, json_parser&) { } - template - inline void iod_from_json_(T& t, json_parser& p) + template + inline void iod_from_json_(S*, T& t, json_parser& p) { p >> fill(t); } - inline void iod_from_json_(std::string& t, json_parser& p) + template + inline void iod_from_json_(S*, std::string& t, json_parser& p) { - p >> '"' >> fill(t) >> '"'; + p >> fill(t); } - inline void iod_from_json_(stringview& t, json_parser& p) + template + inline void iod_from_json_(S*, stringview& t, json_parser& p) { p >> '"' >> fill(t) >> '"'; } - + // Parse a json hashmap ordered the field in the object \o. template inline void iod_attr_from_json_strict(sio& o, json_parser& p) @@ -409,87 +686,104 @@ namespace iod } // Parse a json hashmap. - template - inline void iod_attr_from_json(sio& o, json_parser& p) + template + inline void iod_attr_from_json(const sio<>*, O& /*o*/, json_parser& /*p*/) {} + template + inline void iod_attr_from_json(const sio*, O& o, json_parser& p) { p >> p.spaces; struct attr_info { bool filled; stringview name; }; - attr_info A[std::remove_reference_t::size()]; - - int i = 0; - foreach(o) | [&] (auto& m) + attr_info A[sio::size()]; + + sio scheme;// = *(sio*)(42); + int ai = 0; + foreach(scheme) | [&] (const auto& m) { - A[i].filled = false; + A[ai].filled = false; stringview name(m.symbol().name(), strlen(m.symbol().name())); if (m.attributes().has(_json_key)) { const char* new_name = m.attributes().get(_json_key, _json_key).name(); name = stringview(new_name, strlen(new_name)); } - A[i].name = name; - i++; + A[ai].name = name; + ai++; }; - while (p.peak() != '}') + while (p.peek() != '}') { stringview attr_name; p >> p.spaces >> '"' >> fill(attr_name) >> '"' >> p.spaces >> ':' >> p.spaces; int i = 0; bool attr_found = false; - foreach(o) | [&] (auto& m) + foreach(scheme) | [&] (auto& m) { if (!m.attributes().has(_json_skip) and !attr_found and attr_name == A[i].name) { - iod_from_json_(m.value(), p); + try + { + iod_from_json_(&m.value(), m.symbol().member_access(o), p); + } + catch (const std::exception& e) + { + std::stringstream ss; + ss << "Error when decoding json attribute " << attr_name.to_std_string() << ": " << e.what(); + throw std::runtime_error(ss.str()); + } A[i].filled = true; attr_found = true; } i++; }; - // Fixme: if !attr_found, skip the json value. + // if !attr_found, throw an error. + if (!attr_found) + throw std::runtime_error(std::string("json_decode error: unexpected key ") + + attr_name.to_std_string()); + p >> p.spaces; - if (p.peak() == ',') + if (p.peek() == ',') p.eat_one(); else break; } - if (p.peak() != '}') + if (p.peek() != '}') { - throw p.json_error("Expected } got ", p.peak()); + throw p.json_error("Expected } got ", p.peek()); } - i = 0; - foreach(o) | [&] (auto& m) { - if (!m.attributes().has(_json_skip) and !m.attributes().has(_optional) and !A[i].filled) + ai = 0; + foreach(scheme) | [&] (auto& m) { + if (!m.attributes().has(_json_skip) and !m.attributes().has(_optional) and !A[ai].filled) throw std::runtime_error(std::string("json_decode error: missing field ") + m.symbol().name()); - i++; + ai++; }; } - + // Parse an array. - template - inline void iod_from_json_(std::vector& array, json_parser& p) + template + inline void iod_from_json_(S*, std::vector& array, json_parser& p) { p >> '[' >> p.spaces; - if (p.peak() == ']') + if (p.peek() == ']') { p >> ']'; return; } array.clear(); - while (p.peak() != ']') + while (p.peek() != ']') { T t; - iod_from_json_(t, p); + p >> p.spaces; + iod_from_json_((typename S::value_type*)0, t, p); array.push_back(t); p >> p.spaces; - if (p.peak() == ']') + if (p.peek() == ']') break; else p >> ','; @@ -498,23 +792,30 @@ namespace iod p >> ']'; } - template - inline void iod_from_json_(sio& o, json_parser& p) + template + inline void iod_from_json_(sio* s, O& o, json_parser& p) { p >> p.spaces >> '{'; - iod_attr_from_json(o, p); + iod_attr_from_json(s, o, p); p >> p.spaces >> '}'; } - template - inline void iod_from_json_(sio& o, const std::string& str) + template + inline void iod_from_json_(const S* s, O& o, const std::string& str) { json_parser p(str); if (str.size() > 0) - iod_from_json_(o, p); + iod_from_json_(s, o, p); else throw std::runtime_error("Empty string."); } + + template + inline void iod_from_json_(S*, json_string& s, json_parser& p) + { + p >> s; + } + } template @@ -523,7 +824,7 @@ namespace iod if (o.size() == 0) return; json_internals::json_parser p(str); if (str.size() > 0) - iod_from_json_(o, p); + iod_from_json_((sio*)0, o, p); else throw std::runtime_error("Empty string."); n_read = p.pos; @@ -535,28 +836,84 @@ namespace iod if (o.size() == 0) return; json_internals::json_parser p(str); if (str.size() > 0) - iod_from_json_(o, p); + iod_from_json_((sio*)0, o, p); else throw std::runtime_error("Empty string."); } - + template inline void json_decode(sio& o, std::istringstream& stream) { if (o.size() == 0) return; json_internals::json_parser p(stream); if (stream.str().size() > 0) - iod_from_json_(o, p); + iod_from_json_((sio*)0, o, p); + else + throw std::runtime_error("Empty string."); + } + + template + inline void json_decode(O& o, std::istringstream& stream) + { + if (o.size() == 0) return; + json_internals::json_parser p(stream); + if (stream.str().size() > 0) + iod_from_json_((S*)0, o, p); + else + throw std::runtime_error("Empty string."); + } + + template + inline void json_decode(O& o, const stringview& str) + { + if (S::size() == 0) return; + json_internals::json_parser p(str); + if (str.size() > 0) + iod_from_json_((S*)0, o, p); else throw std::runtime_error("Empty string."); } - + + + template + inline void json_decode(json_string& o, const stringview& str) + { + json_internals::json_parser p(str); + if (str.size() > 0) + iod_from_json_((json_string*)0, o, p); + else + throw std::runtime_error("Empty string."); + } + + template + inline void json_decode(json_string& o, std::istringstream& stream) + { + json_internals::json_parser p(stream); + if (stream.str().size() > 0) + iod_from_json_((json_string*)0, o, p); + else + throw std::runtime_error("Empty string."); + } + template inline std::string json_encode(const sio& o) { - std::stringstream ss; + json_internals::my_ostringstream ss; + json_internals::json_encode_(o, ss); + return ss.move_str(); + } + + template + inline int json_encode(const sio& o, char* buf, int len) + { + json_internals::my_ostringstream ss(buf, len); json_internals::json_encode_(o, ss); - return ss.str(); + return ss.size(); + } + + inline std::string json_encode(const json_string& o) + { + return o.str; } template @@ -565,6 +922,14 @@ namespace iod json_internals::json_encode_(o, stream); } + template + inline std::string json_encode(const std::vector& v) + { + json_internals::my_ostringstream ss; + json_internals::json_encode_(v, ss); + return ss.move_str(); + } + } #endif diff --git a/iod/json_unicode.hh b/iod/json_unicode.hh new file mode 100644 index 0000000..54537b7 --- /dev/null +++ b/iod/json_unicode.hh @@ -0,0 +1,367 @@ +#pragma once + +#include +#include +#include +#include + +namespace iod +{ + using namespace s; + + // struct json_error + // { + // json_error& operator=(const json_error&) = default; + // operator bool() { return code != 0; } + // int code; //const char* what; + // //std::string what; + // }; + + // // json_error make_json_error(const char* what) { return {1, std::string("metajson error: ") + what}; } + // // json_error json_no_error() { return {0, ""}; } + + // json_error make_json_error(const char* what) { return {1}; } + // json_error json_no_error() { return {0}; } + + // static json_error json_ok = json_no_error(); + + // template + // auto make_json_error(W... what) + // { + // // std::stringstream ss; + + // // ss << "metajson error: "; + // // auto add = [&ss] (auto w) { ss << w; }; + // // apply_each(add, what...); + // //return json_error{1, ss.str()}; + // return json_error{1}; + // } + + template + inline decltype(auto) wrap_json_output_stream(O&& s) + { + return D(_append = [&s] (char c) { s << c; }); + } + + inline decltype(auto) wrap_json_output_stream(std::stringstream& s) + { + return D(_append = [&s] (char c) { s << c; }); + } + + inline decltype(auto) wrap_json_output_stream(std::string& s) + { + return D(_append = [&s] (char c) { s.append(1, c); }); + } + + inline decltype(auto) + wrap_json_input_stream(std::stringstream& s) { return s; } + namespace json_internals { + struct json_parser; + } + inline decltype(auto) + wrap_json_input_stream(json_internals::json_parser& s) { return s; } + inline decltype(auto) + wrap_json_input_stream(const iod::stringview& s) { return std::stringstream(s.to_std_string()); } + inline decltype(auto) + wrap_json_input_stream(const std::string& s) { return std::stringstream(s); } + inline decltype(auto) + wrap_json_input_stream(const char* s) { return std::stringstream(std::string(s)); } + + namespace unicode_impl + { + template + auto json_to_utf8(S&& s, T&& o); + + template + auto utf8_to_json(S&& s, T&& o); + } + + template + auto json_to_utf8(I&& i, O&& o) + { + return unicode_impl::json_to_utf8(wrap_json_input_stream(std::forward(i)), + wrap_json_output_stream(std::forward(o))); + } + + template + auto utf8_to_json(I&& i, O&& o) + { + return unicode_impl::utf8_to_json(wrap_json_input_stream(std::forward(i)), + wrap_json_output_stream(std::forward(o))); + } + + enum json_encodings + { + UTF32BE, + UTF32LE, + UTF16BE, + UTF16LE, + UTF8 + }; + + // Detection of encoding depending on the pattern of the + // first fourth characters. + inline auto detect_encoding(char a, char b, char c, char d) + { + // 00 00 00 xx UTF-32BE + // xx 00 00 00 UTF-32LE + // 00 xx 00 xx UTF-16BE + // xx 00 xx 00 UTF-16LE + // xx xx xx xx UTF-8 + + if (a == 0 and b == 0) return UTF32BE; + else if (c == 0 and d == 0) return UTF32LE; + else if (a == 0) return UTF16BE; + else if (b == 0) return UTF16LE; + else return UTF8; + } + + // The JSON RFC escapes character codepoints prefixed with a \uXXXX (7-11 bits codepoints) + // or \uXXXX\uXXXX (20 bits codepoints) + + // uft8 string have 4 kinds of character representation encoding the codepoint of the character. + + // 1 byte : 0xxxxxxx -> 7 bits codepoint ASCII chars from 0x00 to 0x7F + // 2 bytes: 110xxxxx 10xxxxxx -> 11 bits codepoint + // 3 bytes: 1110xxxx 10xxxxxx 10xxxxxx -> 11 bits codepoint + + // 1 and 3 bytes representation are escaped as \uXXXX with X a char in the 0-9A-F range. It + // is possible since the codepoint is less than 16 bits. + + // the 4 bytes representation uses the UTF-16 surrogate pair (high and low surrogate). + + // The high surrogate is in the 0xD800..0xDBFF range (HR) and + // the low surrogate is in the 0xDC00..0xDFFF range (LR). + + // to encode a given 20bits codepoint c to the surrogate pair. + // - substract 0x10000 to c + // - separate the result in a high (first 10 bits) and low (last 10bits) surrogate. + // - Add 0xD800 to the high surrogate + // - Add 0xDC00 to the low surrogate + // - the 32 bits code is (high << 16) + low. + + // and to json-escape the high-low(H-L) surrogates representation (16+16bits): + // - Check that H and L are respectively in the HR and LR ranges. + // - add to H-L 0x0001_0000 - 0xD800_DC00 to get the 20bits codepoint c. + // - Encode the codepoint in a string of \uXXXX\uYYYY with X and Y the respective hex digits + // of the high and low sequence of 10 bits. + + // In addition to utf8, JSON escape characters ( " \ / ) with a backslash and translate + // \n \r \t \b \r in their matching two characters string, for example '\n' to '\' followed by 'n'. + + namespace unicode_impl + { + template + auto json_to_utf8(S&& s, T&& o) + { + // Convert a JSON string into an UTF-8 string. + if (s.get() != '"') + throw std::runtime_error("json_to_utf8: JSON strings should start with a double quote."); + + while (true) + { + // Copy until we find the escaping backslash or the end of the string (double quote). + while (s.peek() != EOF and s.peek() != '"' and s.peek() != '\\') + o.append(s.get()); + + // If eof found before the end of the string, return an error. + if (s.eof()) throw std::runtime_error("json_to_utf8: Unexpected end of string when parsing a string."); + + // If end of json string, return + if (s.peek() == '"') + { + break; + } + + // Get the '\'. + assert(s.peek() == '\\'); + s.get(); + + switch (s.get()) + { + // List of escaped char from http://www.json.org/ + default: + throw std::runtime_error("json_to_utf8: Bad JSON escaped character."); + case '"': o.append('"'); break; + case '\\': o.append('\\'); break; + case '/': o.append('/'); break; + case 'n': o.append('\n'); break; + case 'r': o.append('\r'); break; + case 't': o.append('\t'); break; + case 'b': o.append('\b'); break; + case 'f': o.append('\f'); break; + case 'u': + char a,b,c,d; + + a = s.get(); + b = s.get(); + c = s.get(); + d = s.get(); + + if (s.eof()) + throw std::runtime_error("json_to_utf8: Unexpected end of string when decoding an utf8 character"); + + auto decode_hex_c = [] (char c) { + if (c >= '0' and c <= '9') return c - '0'; + else return (10 + std::toupper(c) - 'A'); + }; + + uint16_t x = + static_cast( + (decode_hex_c(a) << 12) + + (decode_hex_c(b) << 8) + + (decode_hex_c(c) << 4) + + decode_hex_c(d)); + + // If x in the 0xD800..0xDBFF range -> decode a surrogate pair \uXXXX\uYYYY -> 20 bits codepoint. + if (x >= 0xD800 and x <= 0xDBFF) + { + if (s.get() != '\\' or s.get() != 'u') + throw std::runtime_error("json_to_utf8: Missing low surrogate."); + + uint16_t y = static_cast( + (decode_hex_c(s.get()) << 12) + + (decode_hex_c(s.get()) << 8) + + (decode_hex_c(s.get()) << 4) + + decode_hex_c(s.get())); + + if (s.eof()) + throw std::runtime_error("json_to_utf8: Unexpected end of string when decoding an utf8 character"); + + x = static_cast(x - 0xD800U); + y = static_cast(y - 0xDC00U); + + int cp = (x << 10) + y + 0x10000; + + o.append(static_cast(0b11110000 | (cp >> 18))); + o.append(static_cast(0b10000000 | ((cp & 0x3F000) >> 12))); + o.append(static_cast(0b10000000 | ((cp & 0x00FC0) >> 6))); + o.append(static_cast(0b10000000 | (cp & 0x003F))); + + } + // else encode the codepoint with the 1-2, or 3 bytes utf8 representation. + else + { + if (x <= 0x007F) // 7bits codepoints, ASCII 0xxxxxxx. + { + o.append(static_cast(x)); + } + else if (x >= 0x0080 and x <= 0x07FF) // 11bits codepoint -> 110xxxxx 10xxxxxx + { + o.append(static_cast(0b11000000 | (x >> 6))); + o.append(static_cast(0b10000000 | (x & 0x003F))); + } + else if (x >= 0x0800 and x <= 0xFFFF) //16bits codepoint -> 1110xxxx 10xxxxxx 10xxxxxx + { + o.append(static_cast(0b11100000 | (x >> 12))); + o.append(static_cast(0b10000000 | ((x & 0x0FC0) >> 6))); + o.append(static_cast(0b10000000 | (x & 0x003F))); + } + else + throw std::runtime_error("json_to_utf8: Bad UTF8 codepoint."); + } + break; + } + } + + if (s.get() != '"') + throw std::runtime_error("JSON strings must end with a double quote."); + + } + + template + auto utf8_to_json(S&& s, T&& o) + { + o.append('"'); + + auto encode_16bits = [&] (uint16_t b) + { + const char lt[] = "0123456789ABCDEF"; + o.append(lt[b >> 12]); + o.append(lt[(b & 0x0F00) >> 8]); + o.append(lt[(b & 0x00F0) >> 4]); + o.append(lt[b & 0x000F]); + }; + + while (!s.eof()) + { + // 7-bits codepoint + while (s.good() and s.peek() <= 0x7F and s.peek() != EOF) + { + switch (s.peek()) + { + case '"': o.append('\\'); o.append('"'); break; + case '\\': o.append('\\'); o.append('\\'); break; + //case '/': o.append('/'); break; Do not escape / + case '\n': o.append('\\'); o.append('n'); break; + case '\r': o.append('\\'); o.append('r'); break; + case '\t': o.append('\\'); o.append('t'); break; + case '\b': o.append('\\'); o.append('b'); break; + case '\f': o.append('\\'); o.append('f'); break; + default: + o.append(static_cast(s.peek())); + } + s.get(); + } + + if (s.eof()) break; + + // uft8 prefix \u. + o.append('\\'); + o.append('u'); + + uint8_t c1 = static_cast(s.get()); + uint8_t c2 = static_cast(s.get()); + { + // extract codepoints. + if (c1 < 0b11100000) // 11bits - 2 char: 110xxxxx 10xxxxxx + { + uint16_t cp = static_cast(((c1 & 0b00011111) << 6) + + (c2 & 0b00111111)); + if (cp >= 0x0080 and cp <= 0x07FF) + encode_16bits(cp); + else + throw std::runtime_error("utf8_to_json: Bad UTF8 codepoint."); + } + else if (c1 < 0b11110000) // 16 bits - 3 char: 1110xxxx 10xxxxxx 10xxxxxx + { + uint16_t cp = static_cast(((c1 & 0b00001111) << 12) + + ((c2 & 0b00111111) << 6) + + (s.get() & 0b00111111)); + + if (cp >= 0x0800 and cp <= 0xFFFF) + encode_16bits(cp); + else + throw std::runtime_error("utf8_to_json: Bad UTF8 codepoint."); + } + else // 21 bits - 4 chars: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + { + int cp = + ((c1 & 0b00000111) << 18) + + ((c2 & 0b00111111) << 12) + + ((s.get() & 0b00111111) << 6) + + (s.get() & 0b00111111); + + cp -= 0x10000; + + uint16_t H = static_cast((cp >> 10) + 0xD800); + uint16_t L = static_cast((cp & 0x03FF) + 0xDC00); + + // check if we are in the right range. + // The high surrogate is in the 0xD800..0xDBFF range (HR) and + // the low surrogate is in the 0xDC00..0xDFFF range (LR). + assert(H >= 0xD800 and H <= 0xDBFF and + L >= 0xDC00 and L <= 0xDFFF); + + encode_16bits(H); + o.append('\\'); + o.append('u'); + encode_16bits(L); + } + + } + } + o.append('"'); + } + } +} diff --git a/iod/linq.hh b/iod/linq.hh index 1f9c580..aab22c2 100644 --- a/iod/linq.hh +++ b/iod/linq.hh @@ -241,7 +241,7 @@ namespace iod std::vector v; for (const auto& t : group) v.push_back(format_record(req, t)); return v; - }); + }, group); }); }, [] (auto& req, auto f, auto& v) { // If no group by @@ -265,6 +265,13 @@ namespace iod return query(u); } + inline auto from_exp() { return D(); } + + // from(vector, _as(_table_name)) + template + auto from_exp(const function_call_exp<_as_t, E>& e, O&&... tail) + { return cat(from_exp(tail...), D(_as = std::get<0>(e.args))); } + template auto select(const E&... e) { return make_query(cat(q, _select = D(e...))); } @@ -273,13 +280,14 @@ namespace iod template auto group_by(E e) { return make_query(cat(q, _group_by = D(_criteria = e))); } template - auto from(Q table, const E&... e) { return make_query(cat(q, _from = D(_table = &table, e...))); } + auto from(Q&& table, const E&... e) + { return make_query(cat(q, _from = cat(D(_table = &table), from_exp(e...)))); } template - auto inner_join(Q table, E... e) { return make_query(cat(q, - _inner_join = D(_table = &table, e...))); } + auto inner_join(Q&& table, E... e) { return make_query(cat(q, + _inner_join = D(_table = &table, e...))); } template - auto order_by(Q order) { return make_query(cat(q, _order_by = D(_order = order))); } + auto order_by(Q&& order) { return make_query(cat(q, _order_by = D(_order = order))); } template void operator|(F f) { return linq_internals::exec_table(q, f); } diff --git a/iod/linq_evaluate.hh b/iod/linq_evaluate.hh index 40e506c..3175966 100644 --- a/iod/linq_evaluate.hh +++ b/iod/linq_evaluate.hh @@ -73,7 +73,7 @@ namespace iod // Access to attributes of a named variable: variable[attribute] template inline auto - evaluate(const member_accessor_exp& s, const T& ctx) + evaluate(const array_subscript_exp& s, const T& ctx) { return M().member_access(evaluate(s.object, ctx)); } diff --git a/iod/number_symbol_definitions.hh b/iod/number_symbol_definitions.hh index bb364c6..0e3b0a2 100644 --- a/iod/number_symbol_definitions.hh +++ b/iod/number_symbol_definitions.hh @@ -1,72 +1,68 @@ -// Convert the symbol file into a C++ header using sed, or another text processing tool -// with the equivalent command: -// sed -e 's/^\([a-zA-Z1-9_]\+\)/#ifndef IOD_SYMBOL__\1\n#define IOD_SYMBOL__\1\n iod_define_number_symbol(\1)\n#endif/' numbers.sb > number_symbol_definitions.hh - -#ifndef IOD_SYMBOL__1 -#define IOD_SYMBOL__1 +#ifndef IOD_SYMBOL_1 +#define IOD_SYMBOL_1 iod_define_number_symbol(1) #endif -#ifndef IOD_SYMBOL__2 -#define IOD_SYMBOL__2 +#ifndef IOD_SYMBOL_2 +#define IOD_SYMBOL_2 iod_define_number_symbol(2) #endif -#ifndef IOD_SYMBOL__3 -#define IOD_SYMBOL__3 +#ifndef IOD_SYMBOL_3 +#define IOD_SYMBOL_3 iod_define_number_symbol(3) #endif -#ifndef IOD_SYMBOL__4 -#define IOD_SYMBOL__4 +#ifndef IOD_SYMBOL_4 +#define IOD_SYMBOL_4 iod_define_number_symbol(4) #endif -#ifndef IOD_SYMBOL__5 -#define IOD_SYMBOL__5 +#ifndef IOD_SYMBOL_5 +#define IOD_SYMBOL_5 iod_define_number_symbol(5) #endif -#ifndef IOD_SYMBOL__6 -#define IOD_SYMBOL__6 +#ifndef IOD_SYMBOL_6 +#define IOD_SYMBOL_6 iod_define_number_symbol(6) #endif -#ifndef IOD_SYMBOL__7 -#define IOD_SYMBOL__7 +#ifndef IOD_SYMBOL_7 +#define IOD_SYMBOL_7 iod_define_number_symbol(7) #endif -#ifndef IOD_SYMBOL__8 -#define IOD_SYMBOL__8 +#ifndef IOD_SYMBOL_8 +#define IOD_SYMBOL_8 iod_define_number_symbol(8) #endif -#ifndef IOD_SYMBOL__9 -#define IOD_SYMBOL__9 +#ifndef IOD_SYMBOL_9 +#define IOD_SYMBOL_9 iod_define_number_symbol(9) #endif -#ifndef IOD_SYMBOL__9 -#define IOD_SYMBOL__9 +#ifndef IOD_SYMBOL_9 +#define IOD_SYMBOL_9 iod_define_number_symbol(9) #endif -#ifndef IOD_SYMBOL__10 -#define IOD_SYMBOL__10 +#ifndef IOD_SYMBOL_10 +#define IOD_SYMBOL_10 iod_define_number_symbol(10) #endif -#ifndef IOD_SYMBOL__11 -#define IOD_SYMBOL__11 +#ifndef IOD_SYMBOL_11 +#define IOD_SYMBOL_11 iod_define_number_symbol(11) #endif -#ifndef IOD_SYMBOL__12 -#define IOD_SYMBOL__12 +#ifndef IOD_SYMBOL_12 +#define IOD_SYMBOL_12 iod_define_number_symbol(12) #endif -#ifndef IOD_SYMBOL__13 -#define IOD_SYMBOL__13 +#ifndef IOD_SYMBOL_13 +#define IOD_SYMBOL_13 iod_define_number_symbol(13) #endif -#ifndef IOD_SYMBOL__14 -#define IOD_SYMBOL__14 +#ifndef IOD_SYMBOL_14 +#define IOD_SYMBOL_14 iod_define_number_symbol(14) #endif -#ifndef IOD_SYMBOL__15 -#define IOD_SYMBOL__15 +#ifndef IOD_SYMBOL_15 +#define IOD_SYMBOL_15 iod_define_number_symbol(15) #endif -#ifndef IOD_SYMBOL__16 -#define IOD_SYMBOL__16 +#ifndef IOD_SYMBOL_16 +#define IOD_SYMBOL_16 iod_define_number_symbol(16) -#endif \ No newline at end of file +#endif diff --git a/iod/options.hh b/iod/options.hh new file mode 100644 index 0000000..eebf6b1 --- /dev/null +++ b/iod/options.hh @@ -0,0 +1,55 @@ +#pragma once + +#include + +#include "symbol.hh" +#include "grammar.hh" +#include "sio_utils.hh" + +namespace iod +{ + + template + inline auto options(T&&... args); + + struct + { + template + decltype(auto) operator() (X&&... t) const { return options(std::forward(t)...); } + } options_caller; + + template + const S& parse_option(const symbol& s) + { + return *static_cast(&s); + } + + template + auto parse_option(const std::tuple& e) + { + return iod::apply(e, options_caller); + } + + template + auto parse_option(const function_call_exp& e) + { + return make_variable(parse_option(e.args)); + } + + template + auto parse_option(const assign_exp& e) + { + return make_variable(e.right); + } + + template + inline auto options(T&&... args) + { + typedef + sio...> + result_type; + + return result_type(parse_option(args)...); + } + +} diff --git a/iod/parse_command_line.hh b/iod/parse_command_line.hh new file mode 100644 index 0000000..db0d632 --- /dev/null +++ b/iod/parse_command_line.hh @@ -0,0 +1,359 @@ +#include +#include +#include +#include +#include + +#include "stringview.hh" +#include "symbol_definitions.hh" +#include "grammar.hh" + +namespace iod +{ + using namespace s; + + void parse_command_line(int argc, const char* argv[], + std::map>& args_map, + std::vector& positionals) + { + + stringview arg_name; + for (int ai = 1; ai < argc; ai++) + { + const char* arg = argv[ai]; + int len = static_cast(strlen(arg)); + + int ndash = 0; + + if (arg[0] == '-') + ndash = arg[1] == '-' ? 2 : 1; + + if (ndash > 0) + { + arg_name = stringview(arg + ndash, arg + len); + + // if we have an equal assignement -o=12. + for (int i = 0; i < arg_name.size(); i++) + { + if (arg_name[i] == '=') + { + arg_name = stringview(arg_name.str, static_cast(i)); + arg = arg_name.str + i + 1; + len = static_cast(strlen(arg)); + break; + } + } + + // Register swithes. + if (ndash == 1) + { + for (int i = 0; i < arg_name.size(); i++) + args_map[std::string(1, arg_name[i])].push_back("1"); + if (arg_name.size() > 1) + { + arg_name = stringview(); + ndash = 0; + } + } + else + args_map[arg_name.to_std_string()].push_back("1"); + + } + + + if (arg[0] != '-') + { + auto value = stringview(arg, static_cast(len)); + if (arg_name.data()) + { + assert(args_map[arg_name.to_std_string()].size() > 0); + args_map[arg_name.to_std_string()].back() = value; + } + else + positionals.push_back(value); + + arg_name = stringview(); + ndash = 0; + + } + + } + + } + + + template + auto get_option_short_symbol(const assign_exp& o) + { + return o.left; + } + + template + auto get_option_symbol(const assign_exp& o) + { + return o.left; + } + + template + auto get_option_symbol(const assign_exp, V>&) + { + return S1(); + } + + template + auto get_option_short_symbol(const assign_exp, V>&) + { + return S2(); + } + + template + decltype(auto) get_option_value(const assign_exp& o) + { + return o.right; + } + + template + void parse_option_value(stringview str, std::vector& v) + { + v.push_back(boost::lexical_cast(str.str)); + } + + template + void parse_option_value(stringview str, V& v) + { + v = boost::lexical_cast(str.str); + } + + template + void parse_option_value(stringview str, V* v) + { + *v = boost::lexical_cast(str.str); + } + + namespace cl + { + template + auto positionals(T&&... s) + { + return s::_iod_pcl_positionals = std::make_tuple(s...); + } + + template + auto required(T&&... s) + { + return s::_iod_pcl_required = std::make_tuple(s...); + } + + template + auto description(const std::string& gs, T&&... s) + { + return s::_iod_pcl_description = D(_description = gs, + _options = D(s...)); + } + } + + inline const std::string pcl_type_string(const void*) { return "void"; } + inline const std::string pcl_type_string(const std::string*) { return "string"; } + inline const std::string pcl_type_string(const int*) { return "int"; } + inline const std::string pcl_type_string(const float*) { return "float"; } + inline const std::string pcl_type_string(const double*) { return "double"; } + inline const std::string pcl_type_string(const bool*) { return ""; } + template + inline const std::string pcl_type_string(const std::vector*) { + return std::string("vector<") + pcl_type_string((T*)0) + ">"; + } + + template + inline const std::string pcl_type_string(const T*) { return "value"; } + template + inline const std::string pcl_type_string(const T&) { return pcl_type_string((const T*)0); } + + template + auto parse_command_line(std::tuple attrs, int argc, const char* argv[], + T&&... opts) + { + + std::map> args_map; + std::vector positional_values; + parse_command_line(argc, argv, args_map, positional_values); + + auto options = D((get_option_symbol(opts) = get_option_value(opts))...); + auto attrs_sio = iod::apply(attrs, D_caller()); + auto positionals = attrs_sio.get(_iod_pcl_positionals, std::make_tuple()); + + auto required = attrs_sio.get(_iod_pcl_required, std::make_tuple()); + auto description = attrs_sio.get(_iod_pcl_description, D(_description = "", _options = D())); + + // Generation of the help message + auto print_help = [&] () + { + std::cout << "Usage: " << argv[0] << " [options...]"; + foreach(positionals) | [] (auto p) + { + std::cout << " [" << p.name() << "]"; + }; + std::cout << std::endl; + std::cout << description.description << std::endl << std::endl; + std::cout << "Available options:" << std::endl << std::endl; + foreach(std::make_tuple(opts...)) | [&] (auto o) + { + auto opt_symbol = get_option_symbol(o); + auto symbol = std::string(get_option_symbol(o).name()); + auto short_symbol = std::string(get_option_short_symbol(o).name()); + + std::string symbols_str = (symbol.size() > 1 ? "--" : "-") + symbol; + if (short_symbol != symbol) + symbols_str += std::string("|") + (short_symbol.size() > 1 ? "--" : "-") + short_symbol; + + std::string req; + foreach(required) | [&] (auto r) { if (r.equals(opt_symbol)) req += "[REQUIRED] "; }; + + std::string desc = description.options.get(opt_symbol, ""); + std::string type_s = pcl_type_string(options[opt_symbol]); + if (type_s.size()) type_s = " " + type_s; + std::cout << " " << std::setw(25) << std::left << (symbols_str + type_s) << req; + for (unsigned i = 0; i < desc.size(); i++) + { + if (desc[i] != '\n') + std::cout << desc[i]; + else std::cout << std::endl << std::setw(27) << " "; + } + std::cout << std::endl; + }; + }; + + + // Display help on the --help switch. + if (args_map.find("help") != args_map.end()) + { + print_help(); +#ifndef IOD_PCL_WITH_EXCEPTIONS + exit(0); +#endif + throw std::runtime_error("help"); + + } + + auto parse_and_check_value = [&] (auto symbol, stringview elt) + { + try { + parse_option_value(elt, options[symbol]); + } catch (...) + { + std::stringstream ss; + ss << "Invalid command line parameter " << symbol.name() << ": " << std::endl + << " Expected type " << pcl_type_string(options[symbol]) << " got " << elt.to_std_string() + << std::endl; + +#ifndef IOD_PCL_WITH_EXCEPTIONS + std::cerr << ss.str() << std::endl; + exit(1); +#else + throw std::runtime_error(ss.str()); +#endif + } + }; + + // Parse options. + std::map filled; + foreach(std::make_tuple(opts...)) | [&] (auto o) + { + auto symbol = get_option_symbol(o); + auto short_symbol = get_option_short_symbol(o); + + auto it = args_map.find(symbol.name()); + if (it == args_map.end() and + strcmp(short_symbol.name(), symbol.name())) + it = args_map.find(short_symbol.name()); + + if (it != args_map.end() and it->second.size() > 0) + { + for (auto elt : it->second) + parse_and_check_value(symbol, elt); + filled[symbol.name()] = true; + } + else // Positional ? + { + unsigned position = 0; + foreach(positionals) | [&] (auto p) + { + if (p.name() == symbol.name() and + position < positional_values.size()) + { + filled[symbol.name()] = true; + parse_and_check_value(symbol, positional_values[position]); + } + position++; + }; + } + + }; + + // Check required args. + std::vector missing; + foreach(required) | [&] (auto s) + { + if (filled.find(s.name()) == filled.end()) + missing.push_back(s.name()); + }; + + // Error if at least one missing. + if (missing.size() > 0) + { + std::stringstream err; + if (missing.size() > 1) + { + err << "Error missing command line parameters: " << std::endl; + for (auto m : missing) + err << " - " << m << std::endl; + } + else + err << "Error missing required command line parameter " << missing[0] << std::endl; + +#ifndef IOD_PCL_WITH_EXCEPTIONS + std::cerr << err.str() << std::endl; + exit(1); +#endif + throw std::runtime_error(err.str()); + } + + + return options; + } + + + template + auto parse_command_line(std::tuple attrs, + int argc, const char* argv[], + assign_exp<_iod_pcl_positionals_t, PS> ps, + T&&... opts) + { + return parse_command_line(std::tuple_cat(attrs, std::make_tuple(ps)), argc, argv, opts...); + } + + template + auto parse_command_line(std::tuple attrs, + int argc, const char* argv[], + assign_exp<_iod_pcl_required_t, PS> ps, + T&&... opts) + { + return parse_command_line(std::tuple_cat(attrs, std::make_tuple(ps)), argc, argv, opts...); + } + + template + auto parse_command_line(std::tuple attrs, + int argc, const char* argv[], + assign_exp<_iod_pcl_description_t, PS> ps, + T&&... opts) + { + return parse_command_line(std::tuple_cat(attrs, std::make_tuple(ps)), argc, argv, opts...); + } + + + template + auto parse_command_line(int argc, const char* argv[], + T&&... opts) + { + return parse_command_line(std::make_tuple(), argc, argv, opts...); + } +} diff --git a/iod/sio.hh b/iod/sio.hh index 818bb06..c1c6aef 100644 --- a/iod/sio.hh +++ b/iod/sio.hh @@ -7,6 +7,7 @@ #include #include +#include namespace iod { @@ -62,7 +63,7 @@ namespace iod static inline void run(F f, Const&... args) { iod_foreach_runner::run(f, args...); - f(args.template get_nth_attribute()...); + f(args.template get_nth_member()...); } }; @@ -103,7 +104,7 @@ namespace iod iod_foreach_runner::run(g, arg, tail...); } - struct attribute_not_found + struct member_not_found { typedef not_found symbol_type; typedef int value_type; @@ -129,113 +130,80 @@ namespace iod template not_found get_nth() const { return not_found(); } template - const attribute_not_found& get_nth_attribute() const { return *(attribute_not_found*)(0); } + member_not_found get_nth_attribute() const { return member_not_found(); } template - attribute_not_found& get_nth_attribute() { return *(attribute_not_found*)(0); } + member_not_found get_nth_attribute() { return member_not_found(); } template D get(const E&, const D default_) const { return default_; } auto symbols_as_tuple() const { return std::make_tuple(); } }; - template - struct sio : public T, public sio + template + struct sio : public Ms... { - typedef sio self; - typedef sio super; - typedef std::tuple tuple_type; - - enum { _size = 1 + sizeof...(Tail), _empty = 0 }; - - template - struct _nth_attribute_ {}; - - template - struct _nth_attribute_> - { typedef typename std::tuple_element::type type; }; - - template - struct _nth_attribute_= _size)>> - { typedef attribute_not_found type; }; + typedef sio self; + typedef std::tuple tuple_type; - template - using _nth_attribute = typename _nth_attribute_>::type; - - // ----------------------------------------- - // Retrive type of attribute with symbol S. - // ----------------------------------------- - template - struct _attribute_; - - template - struct _attribute_<_size, C, S> - { - typedef attribute_not_found type; - }; + enum { _size = sizeof...(Ms), _empty = sizeof...(Ms) == 0 }; - template - struct _attribute_ - { - typedef _nth_attribute type; - }; + // Get the type of the nth member. + template + using nth_member_type = tl::get_nth_type; - template - struct _attribute_ + template struct pack { - typedef typename _attribute_::symbol_type, S>::type type; + typedef typename T::symbol_type symbol_type; }; - template - using _attribute = typename _attribute_<0, typename _nth_attribute<0>::symbol_type, S>::type; - - template - using attribute_value_type = typename _attribute_<0, typename _nth_attribute<0>::symbol_type, S>::type::value_type; + // get the position of a given symbol + template + using symbol_to_member_type = nth_member_type< + tl::get_type_position::symbol_type...>::value + >; + + // get the value type of a diven symbol. + template + using member_value_type = typename symbol_to_member_type::value_type; - template - struct simple_enum { enum { value = V}; }; + template + struct simple_enum { enum { value = v}; }; - template - using _has = simple_enum, attribute_not_found>::value>; + template + using _has = std::integral_constant::symbol_type...>::value>; // Constructor. inline sio() = default; + inline sio(self&&) = default; + inline sio(const self&) = default; + inline sio(Ms&&... members) : Ms(std::forward(members))... {} + inline sio(const Ms&... members) : Ms(members)... {} - inline sio(const T&& attr, const Tail&&... tail) - : T(attr), - sio(std::forward(tail)...) - { - } - - inline sio(const T& attr, const Tail&... tail) - : T(attr), - sio(std::forward(tail)...) - { - } - - // Get the attribute associated with the symbol S. + // Get the member associated with the symbol S. + // Compile time complexity: O(N) template - auto& symbol_to_attribute(S = S()) + auto& symbol_to_member(S = S()) { - return *static_cast<_attribute*>(this); + return *static_cast*>(this); } template - const auto& symbol_to_attribute(S = S()) const + const auto& symbol_to_member(S = S()) const { - return *static_cast*>(this); + return *static_cast*>(this); } // Access to a member from a symbol. template auto& operator[](const E&) { - return symbol_to_attribute().value(); + return symbol_to_member().value(); } template const auto& operator[](const E&) const { - return symbol_to_attribute().value(); + return symbol_to_member().value(); } template @@ -270,53 +238,37 @@ namespace iod return _size == 0; } - // Access to the super attribute. - super& get_super() { return *this; } - const super& get_super() const { return *this; } - // Get the nth attribute. template - typename std::enable_if&>::type - get_nth_attribute() { return get_super().template get_nth_attribute(); } - + decltype(auto) get_nth_member() { return *static_cast*>(this); } template - typename std::enable_if&>::type - get_nth_attribute() { return *static_cast (this); } + decltype(auto) get_nth_member() const { return *static_cast*>(this); } template - typename std::enable_if&>::type - get_nth_attribute() const { return get_super().template get_nth_attribute(); } - - template - typename std::enable_if&>::type - get_nth_attribute() const { return *static_cast (this); } - - template - const typename _nth_attribute::value_type& - get_nth() const { return get_nth_attribute().value(); } + decltype(auto) + get_nth() const { return get_nth_member().value(); } template - typename _nth_attribute::value_type& - get_nth() { return get_nth_attribute().value(); } + decltype(auto) + get_nth() { return get_nth_member().value(); } - auto&& values_as_tuple() { return std::forward_as_tuple(static_cast(this)->value(), - static_cast(this)->value()...); } - auto&& values_as_tuple() const { return std::forward_as_tuple(static_cast(this)->value(), - static_cast(this)->value()...); } + auto&& values_as_tuple() { return std::forward_as_tuple(static_cast(this)->value()...); } + auto&& values_as_tuple() const { return std::forward_as_tuple(static_cast(this)->value()...); } - auto symbols_as_tuple() const { return std::make_tuple(static_cast(this)->symbol(), - static_cast(this)->symbol()...); } + auto symbols_as_tuple() const { return std::make_tuple(Ms::symbol()...); } // Assignment. + self& operator=(self&& o) = default; template self& operator=(const sio& o) { foreach(o) | [this] (auto& m) { (*this)[m.symbol()] = m.value(); }; return *this; } - + }; + template using has_symbol = typename R::template _has; diff --git a/iod/sio_utils.hh b/iod/sio_utils.hh index 5fc0c59..8a2e4de 100644 --- a/iod/sio_utils.hh +++ b/iod/sio_utils.hh @@ -7,6 +7,9 @@ #include #include +#include +#include +#include #include namespace iod @@ -14,6 +17,37 @@ namespace iod template struct sio; + + template + struct is_sio + { + template + static char test(sio*); + template + static int test(C*); + static const bool value = sizeof(test((std::decay_t*)0)) == 1; + }; + + template + struct is_tuple + { + template + static char test(std::tuple*); + template + static int test(C*); + static const bool value = sizeof(test((std::decay_t*)0)) == 1; + }; + + + template + struct is_symbol + { + template + static char test(symbol*); + static int test(...); + static const bool value = sizeof(test((std::decay_t*)0)) == 1; + }; + template inline auto D(T&&... args); @@ -37,18 +71,38 @@ namespace iod } template - auto exp_to_variable(const function_call_exp& e) + auto exp_to_variable(const function_call_exp& /*e*/) { //return typename S::template variable_type(std::get<0>(c.args)); return typename S::template variable_type()...))>(0); } - + template auto exp_to_variable(const assign_exp& e) { typedef V vtype; + // If V is a symbol, take it as a value and not a ref. + return static_if::value> + ([&] (auto&& v) { return typename S::template variable_type>(); }, + [&] (auto&& v) { return typename S::template variable_type(std::forward(v)); }, + e.right); + } + + template + auto exp_to_variable(const assign_exp>& e) + { + // Fix for clang3.8 where is_symbol> fails to compile. + // error: cannot cast 'std::decay_t >' (aka 'std::tuple') + // to its private base class 'symbol' + typedef decltype(e.right) vtype; return typename S::template variable_type(e.right); } + + template + auto exp_to_variable(const assign_exp&> /*e*/) + { + return typename S::template variable_type(V()); + } template auto exp_to_variable(const assign_exp, V>& e) @@ -57,20 +111,68 @@ namespace iod return typename S::template variable_type()...))>(e.right); } + template + auto exp_to_variable(const assign_exp, V&>& e) + { + typedef V vtype; + return typename S::template variable_type()...))>(e.right); + } + + template + auto remove_variable_ref(S, I, V&& v) + { + return typename S::template variable_type>, I>(v); + } + + template + auto remove_variable_ref(S, I, const char v[]) + { + return typename S::template variable_type(v); + } + + template + auto remove_variable_ref(V&& x) + { + typedef std::remove_reference_t V2; + return static_if::value> + ([] (auto&& x) { return x; }, + [] (auto&& x) { + typedef std::remove_reference_t V3; + return remove_variable_ref(typename V3::symbol_type(), + typename V3::attributes_type(), + x.value()); + }, std::forward(x)); + + } + template inline auto D(T&&... args) { + // Remove reference of values. typedef - sio>...> + sio(args)))) + >...> result_type; - return result_type(exp_to_variable(args)...); + return result_type(remove_variable_ref(exp_to_variable(std::forward(args)))...); } + template + inline auto D_as_reference(T&&... args) + { + // Keep references. + typedef + sio(args)))>...> + result_type; + + return result_type(exp_to_variable(std::forward(args))...); + } + struct D_caller { template - auto operator() (X... t) const { return D(t...); } + auto operator() (X&&... t) const { return D(std::forward(t)...); } }; template @@ -137,14 +239,29 @@ namespace iod template inline auto intersect(const sio& a, - const sio& b) + const sio&) { - return foreach(a) | [] (auto& m) { + return foreach2(a) | [] (auto& m) { return static_if, std::decay_t>::value>( [&] () { return m; }, [&] () { }); }; } + + + template + inline auto remove_symbols(const sio& a, + const std::tuple& b) + { + using res_symbols = typename tuple_minus, + std::decay_t>::type; + + // auto t1 = iod::apply(res_symbols(), D_caller()); + auto t = foreach(res_symbols()) | [&] (auto& s) { + return s = a[s]; + }; + return iod::apply(t, D_caller()); + } template inline std::vector iod_array(const T& t, Tail... args) @@ -155,16 +272,42 @@ namespace iod return res; } - template - struct is_sio + template + auto deep_merge_2_sios(S1 s1, S2) { return s1; } + template + auto deep_merge_2_sios(member_not_found, S2 s2) { return s2; } + template + auto deep_merge_2_sios(S1 s1, member_not_found) { return s1; } + + template + auto deep_merge_2_sios(sio s1, sio s2) + { + auto symbols1 = s1.symbols_as_tuple(); + auto symbols2 = s2.symbols_as_tuple(); + + using symbols_t = typename tuple_minus, std::decay_t>::type; + auto t = foreach(std::tuple_cat(symbols_t(), symbols2)) | [&] (auto s) { - template - static char test(sio*); - template - static int test(C*); - static const bool value = sizeof(test((std::decay_t*)0)) == 1; + return s = deep_merge_2_sios(s1.get(s, member_not_found()), s2.get(s, member_not_found())); }; + return iod::apply(t, D_caller()); + } + + template + auto deep_merge_sios_in_tuple_rec(const std::tuple, O...>& t) + { + return static_if<(I < sizeof...(O))>( + [](const auto& t){ return deep_merge_2_sios(std::get(t), deep_merge_sios_in_tuple_rec(t)); }, + [](const auto& t){ return std::get(t); }, t); + } + + template + auto deep_merge_sios_in_tuple(const std::tuple, O...>& t) + { + return deep_merge_sios_in_tuple_rec<0>(t); + } + } // end of namespace iod. #include diff --git a/iod/stringview.hh b/iod/stringview.hh index 77849fb..398d4bf 100644 --- a/iod/stringview.hh +++ b/iod/stringview.hh @@ -1,20 +1,23 @@ #pragma once +#include #include +#include namespace iod { struct stringview { - stringview() {} + stringview() : str(0), len(0) {} stringview(const std::string& _str) : str(&_str[0]), len(_str.size()) {} - stringview(const char* _str, int _len) : str(_str), len(_len) {} + stringview(const char* _str, std::size_t _len) : str(_str), len(_len) {} + stringview(const char* _begin, const char* _end) : str(_begin), len(static_cast(_end - _begin)) { assert(_end >= _begin); } stringview(const char* _str) : str(_str), len(strlen(_str)) {} bool operator==(const stringview& o) const { return len == o.len and !strncmp(o.str, str, len); } - bool operator==(const std::string& o) const { return len == int(o.size()) and !strncmp(&o[0], str, len); } - bool operator==(const char* o) const { return len == int(strlen(o)) and !strncmp(o, str, len); } + bool operator==(const std::string& o) const { return len == o.size() and !strncmp(&o[0], str, len); } + bool operator==(const char* o) const { return len == strlen(o) and !strncmp(o, str, len); } bool operator<(const stringview& o) const { return strncmp(o.str, str, std::min(len, o.len)); } explicit operator std::string() const { return std::string(str, len); } @@ -22,12 +25,14 @@ namespace iod auto& operator[](int p) { return str[p]; } const auto& operator[](int p) const { return str[p]; } - int size() const { return len; } + int size() const { return static_cast(len); } const char* data() const { return str; } auto to_std_string() const { return std::string(str, len); } + auto substr(int start, int new_len) { return stringview(str + start, static_cast(new_len)); } + const char* str; - int len; + std::size_t len; }; } diff --git a/iod/symbol.hh b/iod/symbol.hh index 9abcd20..21fc82c 100644 --- a/iod/symbol.hh +++ b/iod/symbol.hh @@ -10,13 +10,14 @@ namespace iod { template - struct symbol : public member_accessible, public callable, + struct symbol : public array_subscriptable, public callable, public assignable, public Exp { typedef E symbol_type; constexpr symbol() {} using assignable::operator=; + }; @@ -24,6 +25,12 @@ namespace iod struct variable { }; + + template + decltype(auto) make_variable(V&& value) + { + return typename S::template variable_type(std::forward(value)); + } #define iod_define_symbol_body(SYMBOL, NAME) \ constexpr NAME##_t() {} \ @@ -31,6 +38,9 @@ namespace iod using super::operator=; \ \ inline const char* name() const { return #SYMBOL; } \ + inline constexpr bool equals(_##SYMBOL##_t) const { return true; } \ + template \ + inline constexpr bool equals(T) const { return false; } \ \ template \ inline const auto& member_access(const T& o) const { return o.SYMBOL; } \ @@ -45,11 +55,14 @@ namespace iod \ typedef T value_type; \ typedef INFO attributes_type; \ + typedef variable_type self_type; \ typedef NAME##_t symbol_type; \ + typedef iod::variable> super; \ \ - variable_type() {} \ + variable_type() = default; \ template \ - variable_type(V v) : SYMBOL(v) {} \ + variable_type(V&& v, std::enable_if_t::value and \ + !std::is_same, self_type>::value>* = 0) : SYMBOL(std::forward(v)) {} \ inline value_type& value() { return SYMBOL; } \ inline const value_type& value() const { return SYMBOL; } \ auto symbol() const { return NAME##_t(); } \ @@ -59,22 +72,34 @@ namespace iod value_type SYMBOL; \ }; -#define iod_define_symbol(SYMBOL, NAME) \ +#define iod_define_symbol(SYMBOL) \ namespace s { \ - struct NAME##_t : iod::symbol \ + struct _##SYMBOL##_t : iod::symbol<_##SYMBOL##_t> \ { \ - iod_define_symbol_body(SYMBOL, NAME) \ + iod_define_symbol_body(SYMBOL, _##SYMBOL) \ }; \ - constexpr NAME##_t NAME; \ + constexpr _##SYMBOL##_t _##SYMBOL; \ } + template + struct get_int_symbol_name {}; + template struct int_symbol : iod::symbol> { constexpr int_symbol() {} typedef iod::symbol> super; using super::operator=; + + inline constexpr bool equals(int_symbol) const { return true; } + template + inline constexpr bool equals(T) const { return false; } + static constexpr const int to_int = N; + static const char* name_str_; + inline const char* name() const { + return get_int_symbol_name::value(); + } }; template struct is_int_symbol : std::false_type {}; @@ -82,10 +107,15 @@ namespace iod template struct is_int_symbol : is_int_symbol {}; template struct is_int_symbol : is_int_symbol {}; -#define iod_define_number_symbol(NUMBER) \ - namespace s { \ - typedef iod::int_symbol _##NUMBER##_t; \ - constexpr _##NUMBER##_t _##NUMBER; \ +#define iod_define_number_symbol(NUMBER) \ + namespace iod { template <> \ + struct get_int_symbol_name \ + { \ + static const char* value() { return #NUMBER; } \ + }; } \ + namespace s { \ + typedef ::iod::int_symbol _##NUMBER##_t; \ + constexpr _##NUMBER##_t _##NUMBER; \ } } diff --git a/iod/symbol_definitions.hh b/iod/symbol_definitions.hh index 4489193..083f1c7 100644 --- a/iod/symbol_definitions.hh +++ b/iod/symbol_definitions.hh @@ -1,118 +1,212 @@ -// Convert the symbol file into a C++ header using sed, or another text processing tool -// with the equivalent command: -// sed -e 's/^\([a-zA-Z1-9_]\)\([a-zA-Z1-9_]*\)/#ifndef IOD_SYMBOL__\U\1\L\2\n\#define IOD_SYMBOL__\U\1\L\2\n iod_define_symbol(\1\2, _\U\1\L\2)\n#endif/' symbols.sb > symbol_definitions.hh +// Generated by iod_generate_symbols. +#include +#ifndef IOD_SYMBOL_1 +#define IOD_SYMBOL_1 + iod_define_number_symbol(1) +#endif -#ifndef IOD_SYMBOL__select -#define IOD_SYMBOL__select - iod_define_symbol(select, _select) +#ifndef IOD_SYMBOL_2 +#define IOD_SYMBOL_2 + iod_define_number_symbol(2) #endif -#ifndef IOD_SYMBOL__update -#define IOD_SYMBOL__update - iod_define_symbol(update, _update) + +#ifndef IOD_SYMBOL__compiler_insert_symbols_here__ +#define IOD_SYMBOL__compiler_insert_symbols_here__ + iod_define_symbol(_compiler_insert_symbols_here__) #endif -#ifndef IOD_SYMBOL__insert -#define IOD_SYMBOL__insert - iod_define_symbol(insert, _insert) + +#ifndef IOD_SYMBOL_as +#define IOD_SYMBOL_as + iod_define_symbol(as) #endif -#ifndef IOD_SYMBOL__delete_ -#define IOD_SYMBOL__delete_ - iod_define_symbol(delete_, _delete_) + +#ifndef IOD_SYMBOL_append +#define IOD_SYMBOL_append + iod_define_symbol(append) #endif -#ifndef IOD_SYMBOL__where -#define IOD_SYMBOL__where - iod_define_symbol(where, _where) + +#ifndef IOD_SYMBOL_avg +#define IOD_SYMBOL_avg + iod_define_symbol(avg) #endif -#ifndef IOD_SYMBOL__order_by -#define IOD_SYMBOL__order_by - iod_define_symbol(order_by, _order_by) + +#ifndef IOD_SYMBOL_begin +#define IOD_SYMBOL_begin + iod_define_symbol(begin) #endif -#ifndef IOD_SYMBOL__from -#define IOD_SYMBOL__from - iod_define_symbol(from, _from) + +#ifndef IOD_SYMBOL_compiler_insert_symbols_here__ +#define IOD_SYMBOL_compiler_insert_symbols_here__ + iod_define_symbol(compiler_insert_symbols_here__) #endif -#ifndef IOD_SYMBOL__table -#define IOD_SYMBOL__table - iod_define_symbol(table, _table) + +#ifndef IOD_SYMBOL_condition +#define IOD_SYMBOL_condition + iod_define_symbol(condition) #endif -#ifndef IOD_SYMBOL__order -#define IOD_SYMBOL__order - iod_define_symbol(order, _order) + +#ifndef IOD_SYMBOL_cpt +#define IOD_SYMBOL_cpt + iod_define_symbol(cpt) #endif -#ifndef IOD_SYMBOL__set -#define IOD_SYMBOL__set - iod_define_symbol(set, _set) + +#ifndef IOD_SYMBOL_criteria +#define IOD_SYMBOL_criteria + iod_define_symbol(criteria) #endif -#ifndef IOD_SYMBOL__into -#define IOD_SYMBOL__into - iod_define_symbol(into, _into) + +#ifndef IOD_SYMBOL_description +#define IOD_SYMBOL_description + iod_define_symbol(description) #endif -#ifndef IOD_SYMBOL__values -#define IOD_SYMBOL__values - iod_define_symbol(values, _values) + +#ifndef IOD_SYMBOL_elt +#define IOD_SYMBOL_elt + iod_define_symbol(elt) #endif -#ifndef IOD_SYMBOL__join -#define IOD_SYMBOL__join - iod_define_symbol(join, _join) + +#ifndef IOD_SYMBOL_empty +#define IOD_SYMBOL_empty + iod_define_symbol(empty) #endif -#ifndef IOD_SYMBOL__inner_join -#define IOD_SYMBOL__inner_join - iod_define_symbol(inner_join, _inner_join) + +#ifndef IOD_SYMBOL_end +#define IOD_SYMBOL_end + iod_define_symbol(end) #endif -#ifndef IOD_SYMBOL__group_by -#define IOD_SYMBOL__group_by - iod_define_symbol(group_by, _group_by) + +#ifndef IOD_SYMBOL_exp +#define IOD_SYMBOL_exp + iod_define_symbol(exp) #endif -#ifndef IOD_SYMBOL__on -#define IOD_SYMBOL__on - iod_define_symbol(on, _on) + +#ifndef IOD_SYMBOL_from +#define IOD_SYMBOL_from + iod_define_symbol(from) #endif -#ifndef IOD_SYMBOL__condition -#define IOD_SYMBOL__condition - iod_define_symbol(condition, _condition) + +#ifndef IOD_SYMBOL_group_by +#define IOD_SYMBOL_group_by + iod_define_symbol(group_by) #endif -#ifndef IOD_SYMBOL__else_ -#define IOD_SYMBOL__else_ - iod_define_symbol(else_, _else_) + +#ifndef IOD_SYMBOL_has +#define IOD_SYMBOL_has + iod_define_symbol(has) #endif -#ifndef IOD_SYMBOL__elt -#define IOD_SYMBOL__elt - iod_define_symbol(elt, _elt) + +#ifndef IOD_SYMBOL_inner_join +#define IOD_SYMBOL_inner_join + iod_define_symbol(inner_join) #endif -#ifndef IOD_SYMBOL__cpt -#define IOD_SYMBOL__cpt - iod_define_symbol(cpt, _cpt) + +#ifndef IOD_SYMBOL_join +#define IOD_SYMBOL_join + iod_define_symbol(join) #endif -#ifndef IOD_SYMBOL__as -#define IOD_SYMBOL__as - iod_define_symbol(as, _as) + +#ifndef IOD_SYMBOL_iod_pcl_description +#define IOD_SYMBOL_iod_pcl_description + iod_define_symbol(iod_pcl_description) #endif -#ifndef IOD_SYMBOL__criteria -#define IOD_SYMBOL__criteria - iod_define_symbol(criteria, _criteria) + +#ifndef IOD_SYMBOL_iod_pcl_positionals +#define IOD_SYMBOL_iod_pcl_positionals + iod_define_symbol(iod_pcl_positionals) #endif -#ifndef IOD_SYMBOL__sum -#define IOD_SYMBOL__sum - iod_define_symbol(sum, _sum) + +#ifndef IOD_SYMBOL_iod_pcl_required +#define IOD_SYMBOL_iod_pcl_required + iod_define_symbol(iod_pcl_required) #endif -#ifndef IOD_SYMBOL__avg -#define IOD_SYMBOL__avg - iod_define_symbol(avg, _avg) + +#ifndef IOD_SYMBOL_json_key +#define IOD_SYMBOL_json_key + iod_define_symbol(json_key) #endif -#ifndef IOD_SYMBOL__stddev -#define IOD_SYMBOL__stddev - iod_define_symbol(stddev, _stddev) + +#ifndef IOD_SYMBOL_json_skip +#define IOD_SYMBOL_json_skip + iod_define_symbol(json_skip) #endif -#ifndef IOD_SYMBOL__optional -#define IOD_SYMBOL__optional - iod_define_symbol(optional, _optional) +#ifndef IOD_SYMBOL_len +#define IOD_SYMBOL_len + iod_define_symbol(len) #endif -#ifndef IOD_SYMBOL__json_key -#define IOD_SYMBOL__json_key - iod_define_symbol(json_key, _json_key) +#ifndef IOD_SYMBOL_nth_attribute +#define IOD_SYMBOL_nth_attribute + iod_define_symbol(nth_attribute) #endif -#ifndef IOD_SYMBOL__json_skip -#define IOD_SYMBOL__json_skip - iod_define_symbol(json_skip, _json_skip) + +#ifndef IOD_SYMBOL_on +#define IOD_SYMBOL_on + iod_define_symbol(on) #endif + +#ifndef IOD_SYMBOL_optional +#define IOD_SYMBOL_optional + iod_define_symbol(optional) +#endif + +#ifndef IOD_SYMBOL_options +#define IOD_SYMBOL_options + iod_define_symbol(options) +#endif + +#ifndef IOD_SYMBOL_order +#define IOD_SYMBOL_order + iod_define_symbol(order) +#endif + +#ifndef IOD_SYMBOL_order_by +#define IOD_SYMBOL_order_by + iod_define_symbol(order_by) +#endif + +#ifndef IOD_SYMBOL_r +#define IOD_SYMBOL_r + iod_define_symbol(r) +#endif + +#ifndef IOD_SYMBOL_select +#define IOD_SYMBOL_select + iod_define_symbol(select) +#endif + +#ifndef IOD_SYMBOL_size +#define IOD_SYMBOL_size + iod_define_symbol(size) +#endif + +#ifndef IOD_SYMBOL_str +#define IOD_SYMBOL_str + iod_define_symbol(str) +#endif + +#ifndef IOD_SYMBOL_stream +#define IOD_SYMBOL_stream + iod_define_symbol(stream) +#endif + +#ifndef IOD_SYMBOL_sum +#define IOD_SYMBOL_sum + iod_define_symbol(sum) +#endif + +#ifndef IOD_SYMBOL_t +#define IOD_SYMBOL_t + iod_define_symbol(t) +#endif + +#ifndef IOD_SYMBOL_table +#define IOD_SYMBOL_table + iod_define_symbol(table) +#endif + +#ifndef IOD_SYMBOL_where +#define IOD_SYMBOL_where + iod_define_symbol(where) +#endif + diff --git a/iod/timer.hh b/iod/timer.hh new file mode 100644 index 0000000..fad05ae --- /dev/null +++ b/iod/timer.hh @@ -0,0 +1,32 @@ +#pragma once + +#include + + +namespace vpp +{ + + class timer + { + public: + void start() { start_ = std::chrono::high_resolution_clock::now(); } + void end() { end_ = std::chrono::high_resolution_clock::now(); } + + + unsigned long us() const { + return std::chrono::duration_cast(end_ - start_).count(); + } + + unsigned long ms() const { + return std::chrono::duration_cast(end_ - start_).count(); + } + + unsigned long ns() const { + return std::chrono::duration_cast(end_ - start_).count(); + } + + private: + std::chrono::time_point start_, end_; + }; + +} diff --git a/iod/tuple_utils.hh b/iod/tuple_utils.hh index 42baf0a..2e28620 100644 --- a/iod/tuple_utils.hh +++ b/iod/tuple_utils.hh @@ -2,64 +2,92 @@ #include #include +#include "utils.hh" namespace iod { - template - struct tuple_find_type2; - - // template - // struct tuple_find_type2, X> : public std::integral_constant {}; - template - struct tuple_find_type2, X> : public not_found {}; - - template - struct tuple_find_type2, X> : public std::integral_constant {}; - template - struct tuple_find_type2, X> : public std::integral_constant {}; - template - struct tuple_find_type2, X> : public std::integral_constant {}; - template - struct tuple_find_type2, X> : public std::integral_constant {}; - template - struct tuple_find_type2, X> : public std::integral_constant {}; - - template - struct tuple_find_type2, X&> : public std::integral_constant {}; - template - struct tuple_find_type2, X&&> : public std::integral_constant {}; - template - struct tuple_find_type2, const X&> : public std::integral_constant {}; - template - struct tuple_find_type2, const X> : public std::integral_constant {}; + constexpr int count_first_falses() { return 0; } - template - struct tuple_find_type2, X> : public tuple_find_type2, X> {}; + template + constexpr int count_first_falses(bool b1, B... b) + { + if (b1) return 0; + else return 1 + count_first_falses(b...); + } - template - struct tuple_find_type : public tuple_find_type2<0, T, X> {}; + template + decltype(auto) arg_get_by_type_(void*, + E* a1, T&&... /*args*/) + { + return std::forward(a1); + } - template - auto& tuple_get_by_type(T&& tuple) + template + decltype(auto) arg_get_by_type_(void*, + const E* a1, T&&... /*args*/) { - return std::get>, - std::remove_const_t>>::value>(tuple); + return std::forward(a1); + } + + template + decltype(auto) arg_get_by_type_(void*, + E& a1, T&&... /*args*/) + { + return std::forward(a1); } + template + decltype(auto) arg_get_by_type_(void*, + const E& a1, T&&... /*args*/) + { + return std::forward(a1); + } + + template + decltype(auto) arg_get_by_type_( std::enable_if_t>::value>*, + //void*, + T1&&, T&&... args) + { + return arg_get_by_type_((void*)0, std::forward(args)...); + } - template - struct tuple_embeds : public std::false_type {}; + template + decltype(auto) arg_get_by_type(T&&... args) + { + return arg_get_by_type_>(0, args...); + } + + template + decltype(auto) tuple_get_by_type(std::tuple& tuple) + { + typedef std::decay_t DE; + return std::get, DE>::value)...)>(tuple); + } - template - struct tuple_embeds, U> : public std::false_type {}; - template - struct tuple_embeds, T1> : public std::true_type {}; + template + decltype(auto) tuple_get_by_type(std::tuple&& tuple) + { + typedef std::decay_t DE; + return std::get, DE>::value)...)>(tuple); + } + - template - struct tuple_embeds, U> : public tuple_embeds, U> {}; + template + struct tuple_embeds : public std::false_type {}; + + template + struct tuple_embeds, U> : + public std::integral_constant::value...) != sizeof...(T)> + {}; + template + struct tuple_embeds_any_ref_of : public std::false_type {}; + template + struct tuple_embeds_any_ref_of, U> : public tuple_embeds...>, std::decay_t> {}; + template struct tuple_remove_references; @@ -115,7 +143,90 @@ namespace iod typedef std::tuple type; }; + + template + struct tuple_minus; + + template + struct tuple_minus, std::tuple> + { + typedef typename tuple_remove_elements, R...>::type type; + }; + template using tuple_remove_elements_t = typename tuple_remove_elements::type; + + + template + inline F tuple_map(std::tuple& t, F f, std::index_sequence) + { + return (void)std::initializer_list{((void)f(std::get(t)), 0)...}, f; + } + + template + inline void tuple_map(std::tuple& t, F f) + { + tuple_map(t, f, std::index_sequence_for{}); + } + + template + inline decltype(auto) tuple_transform(T&& t, F f, std::index_sequence) + { + return std::make_tuple(f(std::get(std::forward(t)))...); + } + + template + inline decltype(auto) tuple_transform(T&& t, F f) + { + return tuple_transform(std::forward(t), f, + std::make_index_sequence>{}>{}); + } + + template