|
Boost : |
From: David Bergman (David.Bergman_at_[hidden])
Date: 2006-07-25 12:37:12
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
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk