[Hana] Migrating sqlpp11 to Hana (hypothetically)
Hi Louis,
First of all: Hats off to you, your library and the documentation!
I wanted to play with Hana for quite some time now. The review certainly
offers a good reason to do so :-)
Here's my test project: In sqlpp11 [1], lets assume I wanted to replace
my own implementations of
* compile time strings
* type sets
* functions like any/all/none
with something from Hana.
_Compile__time strings:_
This one seems easy. There probably won't be a C++14 version of sqlpp11,
but there will be a C++17 version, hopefully allowing me to use the
string literal without relying on a non-standard compiler extension.
This is what I currently have [2]:
struct _alias_t
{
static constexpr const char _literal[] = "delta";
using _name_t = sqlpp::make_char_sequence
Roland Bock
Hi Louis,
First of all: Hats off to you, your library and the documentation!
Thank you.
[...]
_Compile__time strings:_ This one seems easy. There probably won't be a C++14 version of sqlpp11, but there will be a C++17 version, hopefully allowing me to use the string literal without relying on a non-standard compiler extension.
This is what I currently have [2]:
struct _alias_t { static constexpr const char _literal[] = "delta"; using _name_t = sqlpp::make_char_sequence
; }; This is what I would like to do:
struct _alias_t { using _name_t = decltype("delta"_s); };
But the whole thing is in a header.
* I do not want to "using namespace ::boost::hana::literals" at top-level or in one of the namespaces in the header.
Is it acceptable to only import the `_s` string literal from Hana? using boost::hana::literals::operator""_s;
* Neither one of the following lines is valid: o using _name_t = decltype(::boost::hana::operator"_s"("delta"));
To be valid, that would have to be:
boost::hana::literals::operator""_s
o using _name_t = decltype("delta"::boost::hana::literals::operator"_s"); o using _name_t = decltype("delta"::boost::hana::literals::operator""_s);
Is there a valid version? Obviously, I would also be happy with something like using _name_t = decltype(ct_string("delta")); * I also cannot use the BOOST_HANA_STRING macro, btw, because that would be a lambda in a decltype...
Yeah, the decltype + lambda restriction is annoying. Frankly, I don't know of any way to use UDLs from another namespace without importing them, and I'm not sure it's feasible.
I want to gradually migrate to Hana, so I'd really like to use a type here, not a variable. But I could not even use a variable, because the following does not compile (clang-3.5.1)
struct A { static constexpr auto hana_name = BOOST_HANA_STRING("cheesecake"); };
Any hints?
You can't use constexpr because of the lambda used by the macro. However, you can do the following: struct A { static constexpr auto hana_name = "cheesecake"_s; }; struct B { using hana_name = decltype("cheesecake"_s); }; Though that requires using the non-standard literal extension. I could add a function to create Hana Strings from a size and a `constexpr char const*`, but you'd be back to your current method.
_type sets:_ Currently I store a bunch of types in type sets to do all kinds of tests at compile time. My type set code will need some cleanup or replacement one fine day. Hana to the rescue...
Er, wait a second. There is no type set, or is there? There is a set for objects (which might represent types). Replacing all those types with objects would take some time, but I could live with that, I guess. But then I would have to replace all my type_sets (which are types themselves) with static constexpr objects, which use tons of constexpr template variables. And there are other constructs which use the types I now have replaced with objects. So I would have to adjust them, too, probably turning even more types into objects.
In theory, that would be the "right" way to do it. What Hana is proposing is a completely new metaprogramming paradigm. However, in practice, it's usually possible to isolate the part of the system using Hana from the rest of the system, or to have Hana interoperate with the rest of the system in some way. I would say this is something that must be handled on a case-per-case basis. For an example of a code base that was partially adapted to Hana with success, see this fork [1] of the Units-BLAS library by Zach Laine.
Can I be sure (given today's compilers) that all these objects stay away from the generated binary?
I guess you can't be __sure__ of it. My view on this is that as long as
the objects are very small (they're basically empty) and constexpr, the
compiler should be able to optimize it away. If that's not the case, and
if this paradigm turns out to be as great as I advertise, perhaps compiler
writers will have more incentive to apply such optimizations. Also, it is
possible that the language will evolve in the direction of making these
things easier.
However, in practice, you can usually ensure that no objects are generated
at all by doing your computations in a function with value-level syntax, and
then using `decltype` on the result of that function. For example, here's
something taken from the Units-BLAS code I linked above. Here's the original
code (edited for simplicity):
------------------------------------------------------------------------------
namespace detail {
template
Also, I did not find some of the methods I would expect for sets:
* Create a joined set * Create an intersect set * Create a difference set * Test if two sets are disjunct * Test if A is a subset of B, no, strike that, just found it in Searchable
I probably could create the methods above with just a few lines of code using the available functions. But it would be convenient to have them available, of course (or did I just miss them?).
This is a simple oversight. Like I said elsewhere, the associative sequences (Set and Map) are still very naive and their interface is incomplete. So far, I have focused on the simplest and most frequently used structure, Tuple. I added this issue [2] to remind me of these missing functions.
By the way, I guess it would be easy for you to compile a list of available methods for each type, e.g. Set. Such a mini-index would be quite helpful IMO.
Good idea. It would be a pain with my current Doxygen setup, but I think I'm about to break up with Doxygen anyway. I noted your idea in this issue [3].
_any/all/none: _Quite a bit of code is tuned to stuff like std::is_same: a type that contains a value (true or false). My current versions of any/all/none work like that, too.
If I wanted to get rid of my own versions, could I use a drop-in from hana? Or would I have to write something like
template
struct my_all { static constexpr bool value = ::boost::hana::all(::boost::hana::make_tuple(B...)); };
The best way would be this:
hana::all(hana::tuple_c
_Intermediate summary:_
* I am not sure if I could replace my current compile-time string yet
It depends on which compromises you are willing to do. As far as I know, you can't do it properly without using the GNU string-literal extension. Oh wait, I could also write a macro similar to BOOST_MPLLIBS_STRING from Metaparse, but that wouldn't be very C++14ish.
* I would like to have a few more convenience methods for Set.
You will get that and much more by the end of the summer, or I won't get my GSoC T-shirt. :-)
* It seems to me like if I wanted to use Hana, I should switch to Hana-style completely. Otherwise it will just be a weird mixture.
Mixing Hana and classic metaprogramming is indeed very difficult for things that have to travel a lot in your interface (e.g. compile-time strings that are exposed to the user). This is simply because Hana is such a different paradigm that if you want your interface to be Hana-like, then you must do it all in this way. However, you can still use Hana under the hood to simplify some metaprogramming locally while keeping your interface as-is. This is essentially what was done in the Units-BLAS project. But of course, the benefit of using Hana will then be limited to those local simplifications. I would be very curious to try and port sqlpp11 to Hana. If you ever want to give it a shot, please let me know. However, that will have to wait for a while because I'm neck deep right now. Regards, Louis [1]: https://github.com/tzlaine/Units-BLAS/pull/1 [2]: https://github.com/ldionne/hana/issues/119 [3]: https://github.com/ldionne/hana/issues/120
Roland Bock
writes: [...]
_Compile__time strings:_ This one seems easy. There probably won't be a C++14 version of sqlpp11, but there will be a C++17 version, hopefully allowing me to use the string literal without relying on a non-standard compiler extension.
This is what I currently have [2]:
struct _alias_t { static constexpr const char _literal[] = "delta"; using _name_t = sqlpp::make_char_sequence
; }; This is what I would like to do:
struct _alias_t { using _name_t = decltype("delta"_s); };
But the whole thing is in a header.
* I do not want to "using namespace ::boost::hana::literals" at top-level or in one of the namespaces in the header. Is it acceptable to only import the `_s` string literal from Hana?
using boost::hana::literals::operator""_s; Not really. This code has to be provided by the user of the library. It
On 2015-06-15 21:08, Louis Dionne wrote: lives outside the sqlpp namespace. I cannot just tell them, to please import some string literal into their namespace. It would be acceptable, if I could just do it inside a struct, but this is illegal: struct A { using boost::hana::literals::operator""_s; }; I could use a function instead of a type and then use decltype when I want the type of the name. struct A { static constexpr auto test() { using boost::hana::literals::operator""_s; return "cheesecake"_s; } }; I will let this sink in a bit...
[...] * I also cannot use the BOOST_HANA_STRING macro, btw, because that would be a lambda in a decltype... Yeah, the decltype + lambda restriction is annoying. Frankly, I don't know of any way to use UDLs from another namespace without importing them, and I'm not sure it's feasible. That would be annoying. In the end, they are functions, aren't they. If it is not possible yet, it should be so in the next standard...
I asked on the standard proposal list if there is anything like that available or proposed yet. [1]
_type sets:_ Currently I store a bunch of types in type sets to do all kinds of tests at compile time. My type set code will need some cleanup or replacement one fine day. Hana to the rescue...
Er, wait a second. There is no type set, or is there? There is a set for objects (which might represent types). Replacing all those types with objects would take some time, but I could live with that, I guess. But then I would have to replace all my type_sets (which are types themselves) with static constexpr objects, which use tons of constexpr template variables. And there are other constructs which use the types I now have replaced with objects. So I would have to adjust them, too, probably turning even more types into objects. In theory, that would be the "right" way to do it. What Hana is proposing is a completely new metaprogramming paradigm. However, in practice, it's usually possible to isolate the part of the system using Hana from the rest of the system, or to have Hana interoperate with the rest of the system in some way. I would say this is something that must be handled on a case-per-case basis.
For an example of a code base that was partially adapted to Hana with success, see this fork [1] of the Units-BLAS library by Zach Laine.
Can I be sure (given today's compilers) that all these objects stay away from the generated binary? I guess you can't be __sure__ of it. My view on this is that as long as the objects are very small (they're basically empty) and constexpr, the compiler should be able to optimize it away. If that's not the case, and if this paradigm turns out to be as great as I advertise, perhaps compiler writers will have more incentive to apply such optimizations. Also, it is possible that the language will evolve in the direction of making these things easier.
However, in practice, you can usually ensure that no objects are generated at all by doing your computations in a function with value-level syntax, and then using `decltype` on the result of that function. For example, here's something taken from the Units-BLAS code I linked above. Here's the original code (edited for simplicity):
[...]
The trick is to enclose your value-level computations inside functions, and then use decltype on your functions to ensure they are never actually called. In practice, doing this in a non-messy way must be studied in a case-per-case basis, or at least I don't have clear guidelines for now. That is a very nice approach for partially migrating (and as you said it might also increase the likelihood that there are no leftovers in the resulting binaries).
Thanks for the detailed reply!
Also, I did not find some of the methods I would expect for sets:
* Create a joined set * Create an intersect set * Create a difference set * Test if two sets are disjunct * Test if A is a subset of B, no, strike that, just found it in Searchable
I probably could create the methods above with just a few lines of code using the available functions. But it would be convenient to have them available, of course (or did I just miss them?). This is a simple oversight. Like I said elsewhere, the associative sequences (Set and Map) are still very naive and their interface is incomplete. So far, I have focused on the simplest and most frequently used structure, Tuple. I added this issue [2] to remind me of these missing functions. Great :-)
By the way, I guess it would be easy for you to compile a list of available methods for each type, e.g. Set. Such a mini-index would be quite helpful IMO. Good idea. It would be a pain with my current Doxygen setup, but I think I'm about to break up with Doxygen anyway. I noted your idea in this issue [3]. Looking forward to it.
_any/all/none: _Quite a bit of code is tuned to stuff like std::is_same: a type that contains a value (true or false). My current versions of any/all/none work like that, too.
If I wanted to get rid of my own versions, could I use a drop-in from hana? Or would I have to write something like
template
struct my_all { static constexpr bool value = ::boost::hana::all(::boost::hana::make_tuple(B...)); }; The best way would be this: hana::all(hana::tuple_c
) This would return a boolean IntegralConstant (which is convertible to bool). This could also be made just as efficient as your clever implementation using std::is_same (that you posted on the list a while ago), because we have the knowledge that the content of the tuple are compile-time bools. However, using `hana::all(hana::make_tuple(b...))` will be comparatively __very__ inefficient, because we can't know what's in the tuple and we must guarantee proper short-circuiting.
Oh, because the it could be tuple_t
_Intermediate summary:_
* I am not sure if I could replace my current compile-time string yet It depends on which compromises you are willing to do. As far as I know, you can't do it properly without using the GNU string-literal extension. Oh wait, I could also write a macro similar to BOOST_MPLLIBS_STRING from Metaparse, but that wouldn't be very C++14ish.
No worries. The current way is OK, and in the long run, I want name-literals anyway [2] :-)
* I would like to have a few more convenience methods for Set. You will get that and much more by the end of the summer, or I won't get my GSoC T-shirt. :-)
Lol, that is a great incentive!
* It seems to me like if I wanted to use Hana, I should switch to Hana-style completely. Otherwise it will just be a weird mixture. Mixing Hana and classic metaprogramming is indeed very difficult for things that have to travel a lot in your interface (e.g. compile-time strings that are exposed to the user). This is simply because Hana is such a different paradigm that if you want your interface to be Hana-like, then you must do it all in this way. However, you can still use Hana under the hood to simplify some metaprogramming locally while keeping your interface as-is. This is essentially what was done in the Units-BLAS project. But of course, the benefit of using Hana will then be limited to those local simplifications.
I would be very curious to try and port sqlpp11 to Hana. If you ever want to give it a shot, please let me know. However, that will have to wait for a while because I'm neck deep right now.
Yes, I am also curious to give it a try. Preferably in combination with Concepts Lite. Oh, that reminds me, I found a serious conceptual problem in ConceptsLite with template-template parameters (at least I think I did). Need to check on that with Andrew Sutton again... I am also drowning in work at the moment. I'll let you know when I get started :-) Cheers, Roland [1] https://groups.google.com/a/isocpp.org/forum/?fromgroups#!topic/std-proposal... [2] https://groups.google.com/a/isocpp.org/forum/#!msg/std-proposals/hYh3hWB0mwg...
Roland Bock
Roland Bock
writes: [...]
_Compile__time strings:_
[...]
* I do not want to "using namespace ::boost::hana::literals" at top-level or in one of the namespaces in the header. Is it acceptable to only import the `_s` string literal from Hana?
using boost::hana::literals::operator""_s; Not really. This code has to be provided by the user of the library. It
On 2015-06-15 21:08, Louis Dionne wrote: lives outside the sqlpp namespace. I cannot just tell them, to please import some string literal into their namespace.
[...]
I must admit I don't have a solution. This is such a pain since it is a __joke__ in terms of implementation difficulty at the compiler/language level, but I think it is not solvable at the library level.
[...]
_any/all/none: _Quite a bit of code is tuned to stuff like std::is_same: a type that contains a value (true or false). My current versions of any/all/none work like that, too.
If I wanted to get rid of my own versions, could I use a drop-in from hana? Or would I have to write something like
template
struct my_all { static constexpr bool value = ::boost::hana::all(::boost::hana::make_tuple(B...)); }; The best way would be this: hana::all(hana::tuple_c
) This would return a boolean IntegralConstant (which is convertible to bool). This could also be made just as efficient as your clever implementation using std::is_same (that you posted on the list a while ago), because we have the knowledge that the content of the tuple are compile-time bools. However, using `hana::all(hana::make_tuple(b...))` will be comparatively __very__ inefficient, because we can't know what's in the tuple and we must guarantee proper short-circuiting.
Oh, because the it could be tuple_t
and you still want this to work in case it short-circuits in the bool element? If so, that sounds quite strange to me.
Notice that I used `tuple_c
[...]
Regards, Louis
Roland Bock
writes: Roland Bock
writes: [...]
_Compile__time strings:_
[...]
* I do not want to "using namespace ::boost::hana::literals" at top-level or in one of the namespaces in the header. Is it acceptable to only import the `_s` string literal from Hana?
using boost::hana::literals::operator""_s; Not really. This code has to be provided by the user of the library. It
On 2015-06-15 21:08, Louis Dionne wrote: lives outside the sqlpp namespace. I cannot just tell them, to please import some string literal into their namespace.
[...] I must admit I don't have a solution. This is such a pain since it is a __joke__ in terms of implementation difficulty at the compiler/language level, but I think it is not solvable at the library level. I do hope that libraries like Hana, Metaparse and sqlpp11 help in
On 2015-06-16 16:09, Louis Dionne wrote: proving that there is need to add language support for compile time strings.
[...]
_any/all/none: _Quite a bit of code is tuned to stuff like std::is_same: a type that contains a value (true or false). My current versions of any/all/none work like that, too.
If I wanted to get rid of my own versions, could I use a drop-in from hana? Or would I have to write something like
template
struct my_all { static constexpr bool value = ::boost::hana::all(::boost::hana::make_tuple(B...)); }; The best way would be this: hana::all(hana::tuple_c
) This would return a boolean IntegralConstant (which is convertible to bool). This could also be made just as efficient as your clever implementation using std::is_same (that you posted on the list a while ago), because we have the knowledge that the content of the tuple are compile-time bools. However, using `hana::all(hana::make_tuple(b...))` will be comparatively __very__ inefficient, because we can't know what's in the tuple and we must guarantee proper short-circuiting.
Oh, because the it could be tuple_t
and you still want this to work in case it short-circuits in the bool element? If so, that sounds quite strange to me. Notice that I used `tuple_c
`, not `tuple_t<...>`. `tuple_c` creates a Tuple containing IntegralConstants, in that case boolean IntegralConstants. Using `tuple_c ` allows the representation of the tuple to be optimized for some types of computations, like `all()` & friends. Using `all(tuple_t<...>)` does not make sense, because `tuple_t<...>` creates a tuple containing types, but types do not have a logical value.
Does that make sense? Got it, thanks!
I'll try to do some more testing before the end of the review (but I am a bit swamped right now). Regards, Roland
On Tue, Jun 16, 2015 at 2:25 PM, Roland Bock
On 2015-06-16 16:09, Louis Dionne wrote:
Roland Bock
writes: Roland Bock
writes: [...]
_Compile__time strings:_
[...]
* I do not want to "using namespace ::boost::hana::literals" at top-level or in one of the namespaces in the header. Is it acceptable to only import the `_s` string literal from Hana?
using boost::hana::literals::operator""_s; Not really. This code has to be provided by the user of the library. It
On 2015-06-15 21:08, Louis Dionne wrote: lives outside the sqlpp namespace. I cannot just tell them, to please import some string literal into their namespace.
[...] I must admit I don't have a solution. This is such a pain since it is a __joke__ in terms of implementation difficulty at the compiler/language level, but I think it is not solvable at the library level.
I do hope that libraries like Hana, Metaparse and sqlpp11 help in proving that there is need to add language support for compile time strings.
This. +1. Then, +1 again. There was a cute suggestion at CppNow about performing Great Violence with the preprocessor (and an illustration was shown for how it does actually work): (1) Put your clear-text in a text file. (This isn't a C++ file, it's just arbitrary text.) (2) Use #include"" to pull that text-file into some arbitrary location within your C++ code (perhaps within a "constexpr" function body). (3) Use "constexpr" operations within that "constexpr" function-body to perform arbitrary string-manipulation (at compile-time), thus achieving compile-time arbitrary string manipulation. There is a "suggested-limit" regarding the number of compile-time execution operations a given compiler might implement to support constexpr-function bodies (I think the guidance was something like 500 operations executed at compile-time by the compiler, don't remember), but you could "reset" this limit with each translation unit. So, I was actually thinking about making a library that would perform this string-manipulation at compile-time to assemble massively complex strings. Typical manipulations are (of course) things like embedding version information, embedding resources and paths, and other higher-order-operations like embedding SQL queries. However, even though I think it would really work, I think it's a horrible work-around that becomes completely useless when we, "doing it right" and actually adding language support for compile-time strings. --charley
Louis Dionne
Roland Bock
writes: Is it acceptable to only import the `_s` string literal from Hana?
using boost::hana::literals::operator""_s; Not really. This code has to be provided by the user of the library. It
On 2015-06-15 21:08, Louis Dionne wrote: lives outside the sqlpp namespace. I cannot just tell them, to please import some string literal into their namespace.
[...]
I must admit I don't have a solution. This is such a pain since it is a __joke__ in terms of implementation difficulty at the compiler/language level, but I think it is not solvable at the library level.
It seems there is the obvious solution of also defining operator"" _boost_hana_s in the global namespace. I guess it is just a question of how common this problem is likely to be.
On 2015-06-17 08:39, Jeremy Maitin-Shepard wrote:
Louis Dionne
writes: Roland Bock
writes: Is it acceptable to only import the `_s` string literal from Hana?
using boost::hana::literals::operator""_s; Not really. This code has to be provided by the user of the library. It
On 2015-06-15 21:08, Louis Dionne wrote: lives outside the sqlpp namespace. I cannot just tell them, to please import some string literal into their namespace.
[...] I must admit I don't have a solution. This is such a pain since it is a __joke__ in terms of implementation difficulty at the compiler/language level, but I think it is not solvable at the library level. It seems there is the obvious solution of also defining operator"" _boost_hana_s in the global namespace. I guess it is just a question of how common this problem is likely to be. Sure, but doesn't that feel like we're back in the dark ages before namespaces?
There was a nice suggestion from Tomasz on the std-proposals list [1]: <quote> [...] However I think that better syntax would be to place the qualification before literal itself, so we would get: auto hello = "hello"s; auto world = std::"world"s; auto any = std::literals::string_literals::"any"s; //If you insist on writing more and auto s = boost::hana::"sample"_s; Having prefixed qualified literal would allow to remove the limittion to use _ in user defined literals, because situation would now be same as for any other entity. </quote> Very cool suggestion :-) Best, Roland [1]: https://groups.google.com/a/isocpp.org/d/msg/std-proposals/hAnVRTlsQZY/1iBWe...
Roland Bock
On 2015-06-17 08:39, Jeremy Maitin-Shepard wrote:
[...]
It seems there is the obvious solution of also defining operator"" _boost_hana_s in the global namespace. I guess it is just a question of how common this problem is likely to be.
Sure, but doesn't that feel like we're back in the dark ages before namespaces?
I would also prefer not putting Hana's literals in the global namespace.
There was a nice suggestion from Tomasz on the std-proposals list [1]:
[...] auto s = boost::hana::"sample"_s; [...]
I think that is a very nice solution. Who's writing the paper? :-) Louis
On 2015-06-17 17:05, Louis Dionne wrote:
Roland Bock
writes: On 2015-06-17 08:39, Jeremy Maitin-Shepard wrote:
[...]
It seems there is the obvious solution of also defining operator"" _boost_hana_s in the global namespace. I guess it is just a question of how common this problem is likely to be. Sure, but doesn't that feel like we're back in the dark ages before namespaces? I would also prefer not putting Hana's literals in the global namespace.
There was a nice suggestion from Tomasz on the std-proposals list [1]:
[...] auto s = boost::hana::"sample"_s; [...] I think that is a very nice solution. Who's writing the paper? :-)
Nobody yet, afaik. I'll put it on the list of things I plan to do once I got a little bit more time to spare. Until then: If anybody wants to start writing, please don't hesitate to ask me for support :-) Cheers, Roland
participants (4)
-
charleyb123 .
-
Jeremy Maitin-Shepard
-
Louis Dionne
-
Roland Bock