473,320 Members | 1,961 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,320 software developers and data experts.

Portable repulsive type extension ?

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.

Nov 14 '05 #1
5 1641
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 <al*******@cs.ucsd.edu> 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 si***@FamOuS.BedBuG.pAlS.INVALID | sed s/[[:upper:]]//g`
Nov 14 '05 #2
"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
Nov 14 '05 #3
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

Nov 14 '05 #4
Arnaud Legrand <al******@cs.ucsd.edu> 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 si***@FamOuS.BedBuG.pAlS.INVALID | sed s/[[:upper:]]//g`
Nov 14 '05 #5
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
Nov 14 '05 #6

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

2
by: Tom Jones | last post by:
I want to write a portable PHP program or two, to do some stuff best done in PHP ... but to commincate with some project code written in C. The function ref i have for PHP is: "PHP Functions...
385
by: Xah Lee | last post by:
Jargons of Info Tech industry (A Love of Jargons) Xah Lee, 2002 Feb People in the computing field like to spur the use of spurious jargons. The less educated they are, the more they like...
5
by: Mark Shelor | last post by:
Problem: find a portable way to determine whether a compiler supports the "long long" type of C99. I thought I had this one solved with the following code: #include <limits.h> #ifdef...
41
by: Phui Hock | last post by:
I'm just curious, why don't we just use uint8_t or uint16_t for example to declare an integer type storage when we know int is always platform-dependent? I do not have programming experience on...
3
by: Shapper | last post by:
Hello, I created a script to upload a file. To determine the file type I am using userPostedFile.ContentType. For example, for a png image I get "image/png". My questions are: 1. Where can...
131
by: pemo | last post by:
Is C really portable? And, apologies, but this is possibly a little OT? In c.l.c we often see 'not portable' comments, but I wonder just how portable C apps really are. I don't write...
20
by: William Ahern | last post by:
Thoughts, comments? #ifdef HAVE_TYPEOF /* * Can use arbitrary expressions */ #define alignof(t) \ ((sizeof (t) > 1)? offsetof(struct { char c; typeof(t) x; }, x) : 1)
1
by: Alexandre Guimond | last post by:
Hi. I want to create a portable setup.py file for windows / linux for an extension package that i need to link with external libraries (gsl and boost). on windows i do something like this: ...
23
by: asit | last post by:
what is the difference between portable C, posix C and windows C ???
0
by: DolphinDB | last post by:
The formulas of 101 quantitative trading alphas used by WorldQuant were presented in the paper 101 Formulaic Alphas. However, some formulas are complex, leading to challenges in calculation. Take...
0
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
1
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
0
by: Vimpel783 | last post by:
Hello! Guys, I found this code on the Internet, but I need to modify it a little. It works well, the problem is this: Data is sent from only one cell, in this case B5, but it is necessary that data...
0
by: ArrayDB | last post by:
The error message I've encountered is; ERROR:root:Error generating model response: exception: access violation writing 0x0000000000005140, which seems to be indicative of an access violation...
1
by: PapaRatzi | last post by:
Hello, I am teaching myself MS Access forms design and Visual Basic. I've created a table to capture a list of Top 30 singles and forms to capture new entries. The final step is a form (unbound)...
0
by: CloudSolutions | last post by:
Introduction: For many beginners and individual users, requiring a credit card and email registration may pose a barrier when starting to use cloud servers. However, some cloud server providers now...
0
by: Shællîpôpï 09 | last post by:
If u are using a keypad phone, how do u turn on JavaScript, to access features like WhatsApp, Facebook, Instagram....
0
by: af34tf | last post by:
Hi Guys, I have a domain whose name is BytesLimited.com, and I want to sell it. Does anyone know about platforms that allow me to list my domain in auction for free. Thank you

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.