Two informal proposals: generate_hierarchy and embed_type

Hi, As I mentioned yesterday, I happened to stay up for a few hours and create two related proposals, after being inspired by Beman's 'identifier' suggestion (as part of his quite useful 'system-0.2' proposal.) I saw - as some people here, including Beman - a potentially wider scope for such embeddings that quite integer-centric "database key" types. The question was whether it was worth the effort. First, one needs a flexible policy system to define what structures to preserve from the underlying type. Such policies need to be injected into the ancestry tree of the embedding type. Thus, we need a hierarchy generator. We have all looked at Andrei's Loki library and its hierarchy generator - I assume. For my purposes, it was actually a bit too specific and data-holding-centric. And, I wanted it to jive with Boost.MPL. [PROPOSAL #1 - generate_hierarchy] So, I created "generate_hierarchy.hpp", which is my proposal for a hierarchy generator. It has two modes of generation: spine-like or chain, as explained in my blog (no, I am *not* promoting my crappy blog, but prefer not to repeat that discussion here...): http://davber.blogspot.com/2006/07/hierarchy-generation-in-c.html. [PROPOSAL #2 - embed_type] After having that hierarchy generator, I then took the rest of the night to create a type embedder, "embed_type.hpp". There is still some work to handle small and big types differently (reference vs. copying) and the naming of policies and such is a bit weird. In short, one can achieve an isomorphic (w.r.t. order, equivalence, arithmetics and conversion) embedding by struct MyInt : embed_type<int, MyInt> { MyInt(int num) : embed_type<int, MyInt>(num) {}; }; int foo = MyInt(42); // will compile! and if you do not want conversion to int, you can do struct MyInt : embed_type<int, MyInt, mpl::remove<embed_policy_all, policy<embed_conversion> > > { MyInt(int num) : embed_type<int, MyInt, ... [ugh, I do not like GCC 3.3 sometimes]>(num) {}; }; int foo = MyInt(42); // will not compile :-( (or should it be :-) since it was expected?) DISCLAIMER: these are quite early drafts of proposals created during one sleepness night, so spare with me. I basically want to know if there is any interest in these libraries and, if so, if they should be proposed as additions to existing Boost libraries or part of new library/ies. The two proposals (two files) with small samples (two files) are attached as a ZIP file (till I gain access to the file vault of Boost.) I will go to bed now, /David

"David Bergman" <David.Bergman@bergmangupta.com> wrote in message news:005801c6afae$a19c4060$a0da1105@davidtp...
Hi,
As I mentioned yesterday, I happened to stay up for a few hours and create two related proposals, after being inspired by Beman's 'identifier' suggestion (as part of his quite useful 'system-0.2' proposal.)
I saw - as some people here, including Beman - a potentially wider scope for such embeddings that quite integer-centric "database key" types.
While integers are often used as the underlying type, any type that meets a few common requirements (CopyConstructible, etc.) can be used. One of my test cases uses std::string. I'm on a road-trip so may not get a chance to look at your stuff for a few days. One thing I'll look at then is to compare several common uses of identifier against your proposal to see if the added generality gets in the way of simple use cases. The other question is how important/useful are the other possible uses of your proposal that go beyond identifier. Cheers, --Beman

Beman wrote:
While integers are often used as the underlying type, any type that meets a few common requirements (CopyConstructible, etc.) can be used. One of my test cases uses std::string.
Yes, but if I wanted to embed the type std::string with my specialization, for some special handling (perhaps to create a Pascal string as its conversion to "char*"? ;-) ), the term (and idea of) 'identifier' does not make much sense. I.e., I felt your proposal - with some modifications and extensions - to be applicable in a far wider scope.
I'm on a road-trip so may not get a chance to look at your stuff for a few days. One thing I'll look at then is to compare several common uses of identifier against your proposal to see if the added generality gets in the way of simple use cases.
The identifier notion could be implemented as typedef mpl::vector< policy<embed_order>, // propagate order policy<embed_equivalence>, // propagate equivalence policy<embed_assignability> // propagate assign-from-raw > ident_policies; template <class T, class D> struct identifier : embed_type<T, D, ident_policies> { identifier(T t) : embed_type<T, D, ident_policies>(t) {}; }; after which 'identifier' should carry your semantics, except for stream operators which are not currently implemented in the embed_type proposal and your clever use of 'unspecified_bool_...'. Ah, I see that you have a boolean negation operator. That could be a useful preservation, so let's define a policy for it: template <T, D, Base> struct embed_neg : Base { bool operator!() const { return !this->value_ref(); } }; and then add it, as template <T, D> struct identifier2 : embed_type<T, D, mpl::push_back<ident_policies, policy<embed_neg> >::type> { identifier2(T t) : embed_type<....>(t) {} }; One could also possibly use SFINAE to bypass policies (read "homomorphisms") not making any sense for the specific embedded type, such as trying to add policy<embed_arithmetics> to struct Foo { void foo(); }; I will investigate that extension.
The other question is how important/useful are the other possible uses of your proposal that go beyond identifier.
I see a lot of proxy/wrapper uses, where most aspects/structures are preserved, but some are not. The policy system also allows for concept-specific homomorphisms to be implemented, building up a portfolio, and then used in concrete cases. An example of a float wrapper where we do NOT want to propagate arithmetics: struct almost_float : embed_type<float, almost_float, mpl::remove<embed_policy_all, policy<embed_arithmetics> > > { almost_float(float f) : embed_type<....>(t) {} }; One could also not only chose not to propagate a behavior but to replace it, such this policy restricting arithmetics to positive values, i.e., enforcing a non-group (algebraically) behavior: template<class T, class D, class Base> struct pos_arith_policy : Base { operator D operator+(const D& rhs) const { return D(this->value_ref() + rhs.value_ref()); } operator D operator-(const D& rhs) const { if (this->value_ref() < rhs.value_ref()) throw std::runtime_error("Oops, we are not in a group anymore, so be careful!"); return D(this->value_ref() - rhs.value_ref()); } }; which can be used to define a special policy list replacing ordinary arithmetics with the checked variant: typedef mpl::vector< mpl::push_back< mpl::remove<embed_policy_all, policy<embed_arithmetics> >, policy<pos_arith_policy> > positive_arithmetics_policies; With this policy list, we can create positive variants of any arithmetic type, such as: struct positive_float : embed_type<float, positive_float, positive_arithmetics_policies> { positive_float(float f) : embed_type<....>(f) { // Make sure we start out with a non-negative float as well... if (f < 0) throw std::runtime_error("If you want positive, please do not start out negative!"); } }; We can now use this positive_float as any float, but with the added positive restriction: positive_float myFloat1(41.0); float temp = myFloat1; // fine, since we inject embed_conversion positive_float myFloat2 = myFloat1 + 1.0; // fine positive_float myFloat3 = myFloat2 - 43.0; // Oops, will trow! One cannot only extend a type with checks, such as these, but also distort behavior, such as with this flipped arithmetics policy, of whose usability one can discuss; perhaps in a stack implementation of some form, or for transparent implementation of a reverse iterator?: template<class T, class D, class Base> struct flip_arith_policy : Base { D operator+(const D& rhs) const { return D(this->value_ref() - this->value_ref()); } D operator-(const D& rhs) const { return D(this->value_ref() + this->value_ref()); } }; Let's use it: struct flipped_int : embed_type<int, flipped_int, mpl::vector<policy<embed_conversion>, policy<flip_arith_policy> > > { flipped_int(int n) : embed_type<...>(n) {} }; flipped_int myInt1 = 43; flipped_int myInt2 = myInt + 1; std::cout << "43 + 1 = " << myInt2 << std::endl; Yes, you guessed it: 42! I hope this triggered some use cases, even though, admittedly, my examples here are pretty lame. /David
participants (2)
-
Beman Dawes
-
David Bergman