Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > Portable repulsive type extension ?

Reply
Thread Tools

Portable repulsive type extension ?

 
 
Arnaud Legrand
Guest
Posts: n/a
 
      11-25-2004
Hello,

I have a question about portability and I have not found the answer in the
FAQ. I have different modules to build. All of them have the same private
part and a common public part (though the implementation in all cases may be
different). Note that I already have implemented a similar idea and that it
works pretty well with my gcc but I don't know whether it is portable or not
(especially with those damn padding bytes... . As a tiny example is better
than a long explanation let me explain you how I actually write it (this is
a brain dead example but it is to explain the idea):

#define PUBLIC \
int (*get)(void* self); \
void (*reset)(void* self)

#define EXTENSION_PLUS \
void (*inc)(void *self); \
void (*dec)(void *self)

#define EXTENSION_TIMES \
void(*double)(void *self); \
void(*divide_by_two)(void *self) \

I know these defines are ugly. That is one of the other reason why I'm
asking beside portability.

With these macros, I can define two different modules with the following
API:

typedef struct {
PUBLIC;
EXTENSION_PLUS;
} s_counter_t, *counter_t;
counter_t counter_new(void);
void counter_free(counter_t c);

and

typedef struct {
PUBLIC;
EXTENSION_TIME;
} s_exponent_t, *exponent_t;
exponent_t exponent_new(void);
void exponent_free(exponent_t e);

In fact, both implementation of these modules include the following common
definition:

#define PRIVATE \
int value
typedef struct {
PRIVATE;
} s_private_common_part_t, *private_common_part_t;

In counter.c, one would find

typedef struct {
PRIVATE;
PUBLIC;
EXTENSION_PLUS;
} s_private_counter_t, *private_counter_t;

counter_new() actually creates a private_counter_t

-------------------------------------
| PRIVATE | PUBLIC | EXTENSION_PLUS |
-------------------------------------
^
|_________
but returns a pointer to here |, i.e. a valid counter_t.

Likewise, in exponent.c, one would find:

typedef struct {
PRIVATE;
PUBLIC;
EXTENSION_TIMES;
} s_private_exponent_t, *private_exponent_t;

exponent_new() would actually creates a private_exponent_t

--------------------------------------
| PRIVATE | PUBLIC | EXTENSION_TIMES |
--------------------------------------

Both module access their PRIVATE values by accessing memory a little bit
before their argument. The nice point with such a way of defining these
modules is that you can then use them seamlessly like that :

counter = counter_new();
exponent = exponent_new();

counter->reset(counter); /* set to 0 */
counter->inc(counter); /* ++ */
counter->get(counter); /* returns 1 */

exponent->reset(exponent); /* set to 1 */
exponent->double(exponent); /* *=2 */
exponent->get(exponent); /* returns 2 */

In this example you always have to give exponent as an argument but in my
particular case, there is always a unique object for each module (at most
one counter and one exponent) and the arguments are generally some other
abstract datatypes (void * is the correct C word for polymorphism, right?.

OK. Now that I have proved you that I knew how to write something ugly, let
me precise that before writing it like that, I were using struct definitions
instead of these #define. But I had to write :

exponent->public.reset(); /* set to 1 */
exponent->extension.double(); /* *=2 */
exponent->public.get(); /* returns 2 */

Even though the version with whole structures is cleaner and definitely
portable, I think that the one I have just written is really convenient for
users (no need to wonder whether you are using a basic functionality or a
extension).

Thanks for reading until here. Any suggestion ?

Arnaud

--
VI VI VI The editor of the beast.

 
Reply With Quote
 
 
 
 
S.Tobias
Guest
Posts: n/a
 
      11-25-2004
I'm not quite sure what your question is and how I could help you,
but I'll write a few remarks, maybe they'll clarify some things
for you.

Arnaud Legrand <(E-Mail Removed)> wrote:

[snippings at different places]

A global remark: `double' is a keyword in C and you cannot
use it as a name for function, variable, member, or anything else.
Let's pretend that `double' is really `Double'.

With structs you can count on two guarantees:
1. First member is at offset 0;
2. In all structs common initial member sequence has the same layout.

> typedef struct {
> PUBLIC;
> EXTENSION_PLUS;
> } s_counter_t, *counter_t;
> counter_t counter_new(void);
> void counter_free(counter_t c);


> typedef struct {
> PUBLIC;
> EXTENSION_TIME;
> } s_exponent_t, *exponent_t;
> exponent_t exponent_new(void);
> void exponent_free(exponent_t e);


> typedef struct {
> PRIVATE;
> } s_private_common_part_t, *private_common_part_t;


> typedef struct {
> PRIVATE;
> PUBLIC;
> EXTENSION_PLUS;
> } s_private_counter_t, *private_counter_t;


> counter_new() actually creates a private_counter_t


> -------------------------------------
> | PRIVATE | PUBLIC | EXTENSION_PLUS |
> -------------------------------------
> ^
> |_________
> but returns a pointer to here |, i.e. a valid counter_t.


Here's a problem: you create s_private_counter_t, but you return
a pointer of type `pointer to s_counter_t' which points to PUBLIC.
First of all, such access via this pointer is Undefined by itself;
second, layouts of PUBLIC+EXTENSION_PLUS in s_counter_t and
s_private_counter_t may be different.

> typedef struct {
> PRIVATE;
> PUBLIC;
> EXTENSION_TIMES;
> } s_private_exponent_t, *private_exponent_t;


> exponent_new() would actually creates a private_exponent_t


> --------------------------------------
> | PRIVATE | PUBLIC | EXTENSION_TIMES |
> --------------------------------------


> Both module access their PRIVATE values by accessing memory a little bit
> before their argument.


For s_private_counter_t and s_private_exponent_t (and
s_private_common_part_t) you can only count on layout of PRIVATE to be
the same.

Suppose the structures were re-written like:
typedef struct
{
PRIVATE;
s_counter_t counter_member;
} s_private_counter_t;
and counter_new() returned pointer to counter_member.

The offsets of PUBLIC in s_private_counter_t and s_private_exponent_t
might be different. This would be not a problem if you knew the
type, but...

> counter = counter_new();
> exponent = exponent_new();


> counter->reset(counter); /* set to 0 */
> counter->inc(counter); /* ++ */
> counter->get(counter); /* returns 1 */


> exponent->reset(exponent); /* set to 1 */
> exponent->double(exponent); /* *=2 */
> exponent->get(exponent); /* returns 2 */


....you might also want to have:
typedef struct { PUBLIC; } s_generic_t;
s_generic_t *generic = (s_generic_t*) counter;
generic->get(generic);
Without knowing the real type of the object _into_ which `generic'
points, you couldn't access `value' in PRIVATE (besides that
it would still be UB).

> abstract datatypes (void * is the correct C word for polymorphism, right?.


<speculation>

"Polymorphism" could only be simulated in C, but implementing this is
just jumping from one kind of ugliness to another.

typedef int (*get_f)(void*self);
/* get() members are to be virtual */
struct Base1 { int value; get_f get; };
struct Base2 { int value; get_f get; };
struct Derived
{
struct Base1 base1;
struct Base1 base1;
int value;
get_f get;
};
int Derived_get(void*self) { return ((struct Derived*)self)->value; }
Now the constructor for Derived has to initialize all three get-s to
the function Derived_get, and then you can access:
Derived *derived = /*...*/;
Base1 *base1 = (Base1*)derived;
derived->get(derived);
base1->get(base1);
For completeness, we must have constructors for Base* types, Base1_get()
and Base2_get(), and up- and down-casts between Base* and Derived
class^H^H^H^H^Hstruct pointers.

In the "dreaded diamond" type inheritance Base1 structs would have to have
struct SuperBase member. This would not be a true virtual member, because
we would have two copies in Derived; it means that all accessor fns (and
we may access SuperBase *only* through these accessors now) to this
class would have to be "virtual" and point to some Derived_somethings(),
that would have to keep the two copies in synch. Since we might want
every struct to be a virtual-base to something, this would have to
hold true for every struct.

Enough. How that could be nice, I don't dare to argue. The Good News
is that there is already a language that supports polymorphism very well.

</speculation>

> exponent->public.reset(); /* set to 1 */
> exponent->extension.double(); /* *=2 */
> exponent->public.get(); /* returns 2 */


Looks good to me.

> Even though the version with whole structures is cleaner and definitely
> portable, I think that the one I have just written is really convenient for
> users (no need to wonder whether you are using a basic functionality or a
> extension).


> Thanks for reading until here. Any suggestion ?


C is a simple language and is very good for simple things (most problems
are simple and can be easily expressed in C). If your problem is complex,
then either simplify the problem or use a language that is suitable for
that problem. I think there is no point in re-inventing C++ in C, it
solves nothing and makes things more difficult than they need to be -
unless it's a mental exercise and a distraction in itself.

(I'm sorry for a too long text.)

--
Stan Tobias
mailx `echo http://www.velocityreviews.com/forums/(E-Mail Removed)LID | sed s/[[:upper:]]//g`
 
Reply With Quote
 
 
 
 
Merrill & Michele
Guest
Posts: n/a
 
      11-26-2004

>"S.Tobias"
> >Arnaud Legrand

[snipped heavily]

> A global remark: `double' is a keyword in C and you cannot
> use it as a name for function, variable, member, or anything else.
> Let's pretend that `double' is really `Double'.


This gets back to identifiers. If only for the case difference you'd be in
trouble, whereas coconuts and olives never offend and can be replaced at the
end of development by words that guide the thinking of the next person who
has to look at the code (even if that is yourself in 5 years).

> C is a simple language and is very good for simple things (most problems
> are simple and can be easily expressed in C). If your problem is complex,
> then either simplify the problem or use a language that is suitable for
> that problem. I think there is no point in re-inventing C++ in C, it
> solves nothing and makes things more difficult than they need to be -
> unless it's a mental exercise and a distraction in itself.


To see polymorphism done in some fashion in C has extraordinary pedagogical
value. It did, however, make me late. MPJ


 
Reply With Quote
 
Arnaud Legrand
Guest
Posts: n/a
 
      11-27-2004
Hello,

On 11/25/04 S.Tobias wrote:

> A global remark: `double' is a keyword in C and you cannot
> use it as a name for function, variable, member, or anything else.
> Let's pretend that `double' is really `Double'.


Sure! I never have compiled the previous example and had not paid attention
to that. I was just looking for a simple example to serve as a basis to my
question. Sorry about that.

> With structs you can count on two guarantees:
> 1. First member is at offset 0;
> 2. In all structs common initial member sequence has the same layout.


OK.

> > -------------------------------------
> > | PRIVATE | PUBLIC | EXTENSION_PLUS |
> > -------------------------------------
> > ^
> > |_________
> > but returns a pointer to here |, i.e. a valid counter_t.

>
> Here's a problem: you create s_private_counter_t, but you return
> a pointer of type `pointer to s_counter_t' which points to PUBLIC.
> First of all, such access via this pointer is Undefined by itself;


Undefined because of the second point, right ?

> second, layouts of PUBLIC+EXTENSION_PLUS in s_counter_t and
> s_private_counter_t may be different.


OK. That was part of my question. It took me a little bit of time (though it
was obious after all) to figure out an example where the layout was
different.

#define PRIVATE \
char foo;

#define PUBLIC \
char bar; \
char *blob

In PRIVATE+PUBLIC, we have &pp.blob - &pp.bar = 3 whereas in PUBLIC only, we
have &p.blob - &p.bar = 4 (on my laptop, with gcc 3.3.4 of course, it may be
different somewhere else). I think that in my case, as there are only
pointers in my structs, there would never be any problem but you're right.
It's definitively unsure. Let's keep it simple and stupid (and clean). I'll
use plain structures.

> > exponent->public.reset(); /* set to 1 */
> > exponent->extension.double(); /* *=2 */
> > exponent->public.get(); /* returns 2 */

>
> Looks good to me.


Yes and to most people I've been talking about it too. Nevermind, let's
forget about my macros then.

Thanks for your comments and your speculation.

Best regards,

Arnaud Legrand

--
The unavoidable price of reliability is simplicity.
-- Hoare

 
Reply With Quote
 
S.Tobias
Guest
Posts: n/a
 
      11-27-2004
Arnaud Legrand <(E-Mail Removed)> wrote:
> On 11/25/04 S.Tobias wrote:


> > With structs you can count on two guarantees:
> > 1. First member is at offset 0;
> > 2. In all structs common initial member sequence has the same layout.


> > > -------------------------------------
> > > | PRIVATE | PUBLIC | EXTENSION_PLUS |
> > > -------------------------------------
> > > ^
> > > |_________
> > > but returns a pointer to here |, i.e. a valid counter_t.

> >
> > Here's a problem: you create s_private_counter_t, but you return
> > a pointer of type `pointer to s_counter_t' which points to PUBLIC.
> > First of all, such access via this pointer is Undefined by itself;


(I should have said: dereferencing this pointer; you can access the object
via this pointer if you cast it properly, eg. to (unsigned char*).)

> Undefined because of the second point, right ?


Sort of yes, because you may not count on anything else but that.
s_private_counter_t and s_counter_t don't have a common initial sequence:
PRIVATE + PUBLIC + EXTENSION
PUBLIC + EXTENSION


> > > exponent->public.reset(); /* set to 1 */
> > > exponent->extension.double(); /* *=2 */
> > > exponent->public.get(); /* returns 2 */

> >
> > Looks good to me.


> Yes and to most people I've been talking about it too. Nevermind, let's
> forget about my macros then.


My intention was not to discourage you; I often do similar things
as you want to do myself - what can be de-uglified, should be. This
is more of a design issue and is not an easy one: first define what
"ugly" and "nice" are.

All I wanted was to show errors in your assumptions, not to suggest
any solution.

When writing my answer I had a feeling that you are mentally mixing
two concepts: data hiding, and interfaces (which includes inheritance)
(I think it can be seen in your naming scheme and your attempts).
C doesn't support access rights - what is declared can be accessed.
I think it might help you to look from interfaces POV, for example:
COMMON - documented, all objects (of a group) share it, it may
be common initial sequence (some parts may be
not documented though, but exist in every instance)
EXTENSION - documented, but may change among subgroups
PRIVATE - not documented, may change, only some special library
functions access this
I don't think there is much reason for PRIVATE to exist, but I
might be wrong (it makes more sense to make "private" (undocumented)
parts within COMMON and EXTENSION).
(This of course does not ensure binary compatibility.)

--
Stan Tobias
mailx `echo (E-Mail Removed)LID | sed s/[[:upper:]]//g`
 
Reply With Quote
 
pete
Guest
Posts: n/a
 
      11-29-2004
Arnaud Legrand wrote:

> #define EXTENSION_TIMES \
> void(*double)(void *self); \
> void(*divide_by_two)(void *self) \
>
> I know these defines are ugly. That is one of the other reason why I'm
> asking beside portability.


It's impossible to terminate the last line of a macro with a backslash.
(because then, it is no longer the last line)

--
pete
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
C extension type gives type error in power operator Paul Moore Python 2 11-20-2008 03:57 PM
Portable printf extension ? lithiumcat@gmail.com C Programming 5 06-05-2008 04:16 PM
Portable Python - free portable development environment ! perica.zivkovic@gmail.com Python 7 01-13-2007 11:19 AM
portable (VHDL) vs. non-portable (altera LPM) approaches to signed computations Eli Bendersky VHDL 1 03-01-2006 02:43 PM
World's Ugliest Dog Dies>> Warning: Repulsive image of ugly dog kenny Computer Support 0 12-08-2005 10:17 PM



Advertisments