Boost logo

Boost :

From: Rob Stewart (stewart_at_[hidden])
Date: 2005-07-26 21:39:31


From: FlSt_at_[hidden]
>
> You can find a new version of my class to the sandbox file vault
> (flos_junction.zip). It implements some of the ideas from this

Don't include the editor swap files in your zip file, please.

> discussion. But it's very inefficient, because it makes a lot of copies.

I don't plan to look at the implementation until we reach some
conclusion as to desirable interface and behavior.

For example, your junction base class template provides size(),
is_empty(), values(), operator container_type(), be(), and
operator bool(). I'm not sure all of those are appropriate.
Indeed, your design requires the junction to contain the values
over which it operates. Why not have it reference the values
over which it operates?

I think it would be better to create a view of a container that
gives it the junction behavior. That avoids copying elements,
though it does require having a separate container with the
elements. (Actually, I can envision the possibility of
constructing a junction in such a way that it creates, populates,
and owns a container if you don't have one already. Then, you
can adapt an existing container or create one on demand.)

That approach obviates the limited container interface you have
created via size(), is_empty(), etc.

BTW, the bool conversion should, probably, use the safe bool
idiom.

> I don't like the implementation, but it works and has the semantic I

Let's discuss the semantics apart from code (other than usage
snippets) for now.

> visualized. Probaly I will need some help to make it more efficient.

I'll gladly help with that when the time comes.

> I see junctions as a expressive way to write comparisons and aritmethic
> operations on lists and as a alternative to loops.

Let's discuss the use cases. That discussion will form the basis
for later documentation, and will focus the design.

> Furthermore I must note that Perls junctions are not limited to sets.
> Junctions are more general. They can be applied to any list and are a
> logical linkage between the elements of the list, which determines the
> result of a comparison. That's exactly what my class implements. There

You make a good point. Junctions are useful to adapt other
containers that match some concepts. That makes them more
general than they otherwise would be. We need to decide what
those concepts are, though.

> are now four classes: conjunction<T>, disjunction<T>, abjunction<T> and
> injunction<T>. Whereas T is a Container-Type. And the Functions all_of,
> any_of, one_of, none_of create instances of this classes from a

Ah, you liked my ideas, eh?

> container. See the examples. There is a base class junction, becaus
> elsewise I don't know how to support all the operators and circumvent
> writing duplicate code (There are already a lot of macros ;-). Is there
> a better way?

I'm not worried about that right now.

My idea is that the junctions are given a range over which to
operate and evaluate to a Boolean when compared with a given
value.

Using a range (Boost.Range) means that whole containers, iterator
pairs, and more can be used to specify the elements that comprise
the data over which the junction operates.

Now let's look at usage:

   if (x == any_of(values))

Here, any_of() creates a disjunction over the supplied values.
The namespace scope equality operator taking a right hand
argument of a disjunction is called with x as the left hand
argument. That might trigger a call to a function on the
disjunction that returns a Boolean, which the equality operator
returns.

   template <typename T>
   bool operator ==(T const & lhs_i, disjunction<T> const & rhs_i)
   {
      // do work
      // return answer
   }

Since a call like the above is the likely usage, you don't want
to copy the elements into the junction. You just want their
extent. Then, the equality operator (indirectly?) iterates the
range to figure out the correct answer.

A complication is when comparing two junctions:

   if (none_of(values1) == any_of(values2))

This requires the cooperation of two junctions, if we forward to
the junctions to get the answer. I suspect the better design is
for the equality operators to do all of the work.

   template <typename T>
   bool operator ==(injunction<T> const & lhs_i, disjunction<T> const & rhs_i)
   {
      // do work
      // return answer
   }

The equality operators can be friends of the junction classes so
that they can iterate the elements. Just knowing which
combination of types are used for the equality is sufficient to
invoke the correct algorithm.

So, for my first example, the equality operator would iterate the
values looking for one that equals x. For my second example, the
operator that expression invokes would iterate the values in
values1 and check each one against all of the values in values2.
Indeed, I see reuse there: for each element in values1, it
degenerates to the first example.

What do you think of this approach? Boost.Range gives us a wide
range of types over which to operate, and putting the logic in
the equality operators rather than the junction types gives us
compile-time logic selection. The junction types merely serve as
selectors for the appropriate equality operator.

When I was writing the above, I had in mind that any_of() was a
function template that created a disjunction. However, the class
template could be called any_of. That way, "any_of(...)" is a
constructor call; its use in the examples above creates temporary
instances on which the equality operator can be invoked.

-- 
Rob Stewart                           stewart_at_[hidden]
Software Engineer                     http://www.sig.com
Susquehanna International Group, LLP  using std::disclaimer;

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk