Pascal-like set class

R

Rich Herrick

The latest version of my "Pascal-like" set class is available here:

http://www.richherrick.com/software/herrick-1.0.zip

Those from the old YAHOO BOOST forum might remember it from several months
back. I have enhanced the code, and "deboostifized" it.

For those unfamiliar with it, it is a template class that implements set
classes that are similar to the set type in the Pascal language. Like
Pascal, any integer (ordinal) type may be used with the set, but I mainly
wrote it for use with enumerations. Set elements are stored at the bit
level, so it is every compact. There is even a member template for defining
set objects as compile-time constants. I wrote it because none of the C++
containers (i.e., set and bitset) suited my needs.

A big change from older versions is the addition of a traits template
parameter for use gaining finer control over the class' behavior for such
things as bit ordering and element range (which defaults to [0,
number-of-elements - 1]).

Examples from documentation:

declaration:
enum color_type
{NON_COLOR = -1, RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET,
NUM_COLORS};

typedef herrick::pascal_set<color_type, NUM_COLORS> color_set;

compile-time constants:
const color_set additive_primaries =
color_set::setof<RED, GREEN, BLUE>::value;

const color_set subtractive_primaries =
color_set::setof<RED, YELLOW, BLUE>::value;

I'd be very interested in comments about the code, the interface (I may have
went overboard on operators!), or my use of templates (I still get hazy on
when to use "typename" and "template").

My goal is to have the interface as much as like the Pascal set type as
feasible, while having the code conform to the lated standard (regardless
particular compiler conformity).

The code currenty compilers with:
GCC 3.4.0 (MinGW)
Visual C++ Toolkit 2003


Rich Herrick
(e-mail address removed)
 
D

David Hilsee

message
I'd be very interested in comments about the code, the interface (I may have
went overboard on operators!), or my use of templates (I still get hazy on
when to use "typename" and "template").
<snip>

I think you did go overboard on the operators. Most of the operators for
that class do not stick to their traditional meaning. Some are close calls,
but the definitions of operator bool, !, <, <=, >, >=, <<, >>, /, and * look
odd to me.
 
R

Rich Herrick

David Hilsee said:
message
<snip>

I think you did go overboard on the operators. Most of the operators for
that class do not stick to their traditional meaning. Some are close calls,
but the definitions of operator bool, !, <, <=, >, >=, <<, >>, /, and * look
odd to me.

Thanks for replying. All the relational ones match how Pascal defines them,
and are needed to do subset/superset comparisions. The / and * operators
are also how Pascal defines them for doing symmetric difference and
intersection, respectively. There really isn't an operator bool, its really
operator void*, which will allow testing for a non-empty set in an if
statement without the nasty side-effects you will get with operator bool.
Though I will admit that operator void* and operator ! aren't really needed,
as there is a member that will test for an empty set (and you could also
test for equals to NULL_SET). The ones I knew someone would question are >>
and <<, as I almost took them out several times. But I just like the way
they "look" for adding elements to a set. There is a + operator that does
the same thing, but I think "S + e1 + e2" looks confusing, especially if e1
and e2 are integers: S + 1 + 2. I think S << 1 << 2 appears more readable
in code. Besides, the former creates a new set and the later does not, and
S += 1 + 2 will, of course, not work. Though again there are many other
ways to add elements to the set such as: S *= set_type(e1, e2), which will
union S with the new set, the result of which is in S.

Thanks,

Rich Herrick
 
D

David Hilsee

message
Thanks for replying. All the relational ones match how Pascal defines them,
and are needed to do subset/superset comparisions. The / and * operators
are also how Pascal defines them for doing symmetric difference and
intersection, respectively. There really isn't an operator bool, its really
operator void*, which will allow testing for a non-empty set in an if
statement without the nasty side-effects you will get with operator bool.
Though I will admit that operator void* and operator ! aren't really needed,
as there is a member that will test for an empty set (and you could also
test for equals to NULL_SET). The ones I knew someone would question are
and <<, as I almost took them out several times. But I just like the way
they "look" for adding elements to a set. There is a + operator that does
the same thing, but I think "S + e1 + e2" looks confusing, especially if e1
and e2 are integers: S + 1 + 2. I think S << 1 << 2 appears more readable
in code. Besides, the former creates a new set and the later does not, and
S += 1 + 2 will, of course, not work. Though again there are many other
ways to add elements to the set such as: S *= set_type(e1, e2), which will
union S with the new set, the result of which is in S.

I'll buy the other operators, but I think that <<, >>, !, nad operator void*
are, at the very least, questionable. Also, the == and != for set
membership seem odd, because I pronounce the code "If element e is equal to
set s" when it really means "if set s contains element e".

A few more questions:

Why does the traits class provide the array element type? Why would anyone
want to customize the element?

Why even bother with an array? Why does std::bitset not provide what you
need? It took me a few minutes to wrap my head around the "traits" code,
and then I realized that it was basically implementing std::bitset. I think
that's what it's doing, anyway.
 
R

Rich Herrick

David Hilsee said:
message <snip>
I'll buy the other operators, but I think that <<, >>, !, nad operator void*
are, at the very least, questionable. Also, the == and != for set
membership seem odd, because I pronounce the code "If element e is equal to
set s" when it really means "if set s contains element e".
I agree. To be honest, I had forget about the == and != operators, and in
my code I use the contains() member. I think I will remove them on future
releases. But I still waffle back and forth on << and >>. I can see where
they are extraneous, but I tend to like and use them a lot. The ! and void*
operator I will probably remove also.
A few more questions:

Why does the traits class provide the array element type? Why would anyone
want to customize the element?
Because, the reason I wrote it was because I wanted low-level control of the
layout and the size. If I have a set with n items and n <= CHAR_BIT, I may
want to fit them all into am unsigned char instead of an unsigned int. I do
a lot of embedded software, dealing with drivers and memory-mapped registers
and such. I'm use to have that kind of control, and tend to write
interfaces with that in mind. I also deal with resources that are more
constrained than an app. writer on a PC might. So, I guess the simple
answer to your question is they wouldn't, except when the array is only one
element in size, then I may want to use the smallest int type I can.

Why even bother with an array? Why does std::bitset not provide what you
need? It took me a few minutes to wrap my head around the "traits" code,
and then I realized that it was basically implementing std::bitset. I think
that's what it's doing, anyway.
Depends on what you are asking. If you are asking why pascal_set instead of
bitset, then the answer is pascal_set is more typesafe then bitset and I
want to mimic the Pascal set type as closely as I could, although I suppose
I could have just wrapped bitset inside pascal_set. If you are asking why I
didn't just wrap bitset inside my class instead of using an array, it comes
down to control. I wanted control of the size and control of the bit-order.
A lot of what I do interfaces with other languages (I write a lot of C, C++
and Ada--both '83 and '95), and memory-mapped registers. I have to know the
layout. pascal_set makes it nice when dealing with set of bit flags in a
memory-mapped register or an Ada bit array. Though its implementation
specific, Ada compilers tend to use the MSB as bit 0 on a big-endian machine
and the LSB as bit 0 on a little-endian machine. I wanted to be able to set
my bit order to match. Plus, I want to see if I could use templates to
create compile-set constant sets, which I may not be able to do with bitset.
This is how the setof template evolved.

My reasons probably aren't good enough for someone looking for a set class
to choose mine over bitset or even set. But that's OK, I wrote it for me,
and found it useful and a joy to use and thought to share it with anyone you
may be interested. I am currently using it recreationally in writing some
Interactive Fiction.

And while writing this, I just thought of a big bug in the whole "traits"
thing I must now fix....

Regards.
 
R

Rich Herrick

By the way David, what is your opinion of the constructors provided? Too
much?

Thanks
 
D

David Hilsee

message
Because, the reason I wrote it was because I wanted low-level control of the
layout and the size. If I have a set with n items and n <= CHAR_BIT, I may
want to fit them all into am unsigned char instead of an unsigned int. I do
a lot of embedded software, dealing with drivers and memory-mapped registers
and such. I'm use to have that kind of control, and tend to write
interfaces with that in mind. I also deal with resources that are more
constrained than an app. writer on a PC might. So, I guess the simple
answer to your question is they wouldn't, except when the array is only one
element in size, then I may want to use the smallest int type I can.

I understand that, but I'm surprised that there would be cases where you
would want to use an unsigned char, and other cases where you would want to
use an unsigned int. In the latter case, wouldn't you just use multiple
unsigned chars? I've never done embedded programming, so I might be
overlooking something. Is there an alignment issue?
 
D

David Hilsee

Rich Herrick said:
By the way David, what is your opinion of the constructors provided? Too
much?
<snip>

I don't see any problem with them. Many might be unnecessary, though,
thanks to the ones that take arrays. I can see how they look a tad nicer
than the equivalent code that uses arrays, though.
 
R

Rich Herrick

I understand that, but I'm surprised that there would be cases where you
would want to use an unsigned char, and other cases where you would want to
use an unsigned int. In the latter case, wouldn't you just use multiple
unsigned chars? I've never done embedded programming, so I might be
overlooking something. Is there an alignment issue?
Well, one reason would be a memory-mapped register accessed across a VME
bus. If a 16-bit register that is mapped as a D16 access, but you attempt
to access it as a D32 or 2 D8, you will get a data access exception. Same
with an 8-bit register. So, if I make the underlining type match (which
again is implementation specific), I can access it with my class.

If I had a large set, then sure, I would use int, because the set operations
are done on a whole int at a time, not bit. So, on a platform where int is
32 bits, and I had a set with 64 elements, it would do a union in two
operations, not 64. But, on the other hand, if I may be more concerned with
size. If, assuming char is 8 bits, and I need my set to fit into 24 bits
(maybe its in a struct talking to an Ada record, and Ada had a
representation specification that maps it into 24 bits), I can achieve this
by setting the unit type to unsigned char. I would be trading efficiency to
make it fit, but that would probably not be an issue.
 
R

Rich Herrick

David Hilsee said:
<snip>

I don't see any problem with them. Many might be unnecessary, though,
thanks to the ones that take arrays. I can see how they look a tad nicer
than the equivalent code that uses arrays, though.
I wrote the multitude of constructors that take different numbers of
elements for type-safety (over var. length args.) and efficiently. I was
wondering if maybe the array ones were too much (especially since there is
two of them).

Thanks for your insights.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top