Portable repulsive type extension ?

Discussion in 'C Programming' started by Arnaud Legrand, Nov 25, 2004.

  1. 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.
     
    Arnaud Legrand, Nov 25, 2004
    #1
    1. Advertising

  2. Arnaud Legrand

    S.Tobias Guest

    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 <> 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 LID | sed s/[[:upper:]]//g`
     
    S.Tobias, Nov 25, 2004
    #2
    1. Advertising


  3. >"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
     
    Merrill & Michele, Nov 26, 2004
    #3
  4. 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
     
    Arnaud Legrand, Nov 27, 2004
    #4
  5. Arnaud Legrand

    S.Tobias Guest

    Arnaud Legrand <> 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 LID | sed s/[[:upper:]]//g`
     
    S.Tobias, Nov 27, 2004
    #5
  6. Arnaud Legrand

    pete Guest

    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
     
    pete, Nov 29, 2004
    #6
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Eli Bendersky
    Replies:
    1
    Views:
    1,173
    Mike Treseler
    Mar 1, 2006
  2. Anand
    Replies:
    3
    Views:
    903
    Tim Daneliuk
    Nov 8, 2003
  3. Replies:
    7
    Views:
    921
  4. Portable printf extension ?

    , Jun 4, 2008, in forum: C Programming
    Replies:
    5
    Views:
    445
    Keith Thompson
    Jun 5, 2008
  5. Paul  Moore
    Replies:
    2
    Views:
    268
    Paul Moore
    Nov 20, 2008
Loading...

Share This Page