
I'm not really getting the interface that your input/output transformers would expose, though. Let's take this one, for example:
template <> struct OutputTransformer<NullAsEmptyString> { using value_type = std::string;
static std::string get_value( int /*index_*/ ) { static std::string x = "hello"; return x; }; static void get_value( int index_, std::string& value_ ) { value_ = get_value( index_ ); }; };
I assume this is user-provided code. Where does the user get the string value from? What does index_ mean in this context?
sorry for beeing not clear (and sending a not directly fitting example)
that code should be library code - more a less a collection of base-mysql concepts that can be used - this sample transformer lets you act empty strings as null in mysql - the implementation is a dummy - only to get a feeling how the data-flow is
my adaption is used with SQLite and the index is the parameter index that would then map to SQLite bind functions or as in this case checks if the value is null and returns ""
plus serveral other "typical" helper for adaption problems
What kind of transformers do you propose? For instance, something to make a NULL string be treated as empty is a pattern I don't especially like. I guess string-to-json parsing is the obvious one. That would end up having the form of custom types that can be specified in the tuple/struct representing a row, so something like: tuple<int, std::string, null_as_empty_string> row; resultset.read_one(row);
to know as much as possible before-hand - allows maybe deeper optimization etc. for example the my_select instance can use prepared statements per default (and this is connection oriented with sqlite)
I'd say you know at compile-time as much in both cases.
// return value tuple const auto [ein_int2, ein_float2, ein_string2] = my_select( { 123 } );
I'd suggest something like struct my_row { int ein_int2; float ein_float2; string ein_string2; }; BOOST_DESCRIBE_STRUCT(my_row); resultset.read_one(row); Returning it by value works great for sync ops but can have bad implications in async ops.
// multi row fetch using My_select = Prepared_fetch<std::tuple<int>, std::tuple<int, float, NullAsEmptyString>>; My_select my_select( db_connection, "select a, b from test where c == ?1" );
std::vector<Result2> result; my_select.fetch_copy( std::back_inserter( result ), 34 ); my_select.fetch_copy( result, 34 );
auto fetch_func = []( const int& /*ein_int_*/, const float& /*ein_float_*/, const std::string& /*ein_string_*/ ) {}; my_select.fetch_func( fetch_func, 34 ); auto fetch_func_cancel = []( const int& /*ein_int_*/, const float& /*ein_float_*/, const std::string& /*ein_string_*/ ) { return false; }; my_select.fetch_func_with_cancel( fetch_func_cancel, 34 );
I can see something like fetch_copy, as a generalization of resultset::read_many. Other users have also brought it up. https://github.com/anarthal/mysql/issues/58 tracks it. It can be extended to work with tuples or structs.
Boost does only provide low level stuff for real low level concepts (smart-pointer, maps etc.-) but most other libraries are always introducing very high level concepts
As Mateusz pointed out, I think there is room for everything - Beast has very low level stuff, too. Thanks, Ruben.