An excursion to pseudo-polymorphism

G

guillemort

We have a system of small objects in the lines of

typedef struct {
...
} text_object;

And we have a "object master" that serves as a container for
several of these small objects, in the lines of

typedef struct {
...
text_object *text;
media_object *media;
...
} object_master;

Now, we need a generic/polymorphic way to initialise the small objects
as the initialisation is similar for all of them. I would like to use
inlined functions instead of macros for that, but currently don't see
any natural, clean way for it. A pseudo-implementation with a macro
(ALLOC_OBJ) is provided below.

Any ideas for converting that macro to an inline function?

my_error alloc_text(object_master *om, params* pms)
{
my_errcode err = 0;
text_object *to;
ALLOC_OBJ(om, to, textobj, TEXT_OBJECT_TYPE, pms);
return err;
}

my_error alloc_text(object_master *om, params* pms)
{
my_errcode err = 0;
media_object *mo;
ALLOC_OBJ(om, mo, mediaobj, MEDIA_OBJECT_TYPE, pms);
return err;
}

#define ALLOC_OBJ(objmaster, gen_obj, gen_obj_field, gen_obj_type,
params)
{
my_ASSERT(obj != NULL);
my_ASSERT(params != NULL);

gen_obj = my_malloc(sizeof(*gen_obj));
if (! gen_obj)
return ERR_OOM;

my_memset(gen_obj, 0, sizeof(*gen_obj));

gen_obj->module = my_module_find(params);
if (! gen_obj->module) {
err = ERR_MISSING_MODULE;
goto out_free_gen_obj;
}
if (my_typeof(gen_obj->module) != gen_obj_type) {
err = ERR_WRONG_MODULE;
goto out_free_gen_obj;
}

err = gen_obj->module->mod_init(gen_obj, params);
if (err)
goto out_free_gen_obj;

goto out;

out_free_gen_obj:
my_memset(gen_obj, 0, sizeof(*gen_obj));
my_free(gen_obj);
gen_obj = NULL;

out:
/* especially rigorous w/o a macro */
objmaster->gen_obj_field = gen_obj;
}
 
T

Tom St Denis

We have a system of small objects in the lines of

typedef struct {
...
} text_object;


You're trying to emulate C++, the best way is to use a plugin model.
Prepare structures that have the address of functions that do init,
deinit, etc. Then just call through them.

Tom
 
G

guillemort

Tom said:
You're trying to emulate C++, the best way is to use a plugin model.
Prepare structures that have the address of functions that do init,
deinit, etc. Then just call through them.

Tom

I'm sorry, but this is too vague. I can't see how function pointers can
help in this case. ALLOC_OBJECT() is the problem, as it's generic. No
use of making it a "member function" by a function pointer as there is
nothing object-specifc going on, only identifiers/types change.

Can you perhaps bring an example?
 
T

Tom St Denis

I'm sorry, but this is too vague. I can't see how function pointers can
help in this case. ALLOC_OBJECT() is the problem, as it's generic. No
use of making it a "member function" by a function pointer as there is
nothing object-specifc going on, only identifiers/types change.

Can you perhaps bring an example?

http://libtomcrypt.com

That entire library is pluggable. For instance, my HMAC message
authentication code ... er source code [hehehe] doesn't know what hash
you are using. Doesn't know how the hash even works. Just knows it
can call certain functions to handle the message being hashed.

Similarly my Public Key code has no clue how your bignum math is done
[I support three different math libraries, each with their own bignum
data types, init/deinit functions, etc].

etc, etc, etc.

You're just giving up too easily.

Tom
 
G

guillemort

Tom said:
I'm sorry, but this is too vague. I can't see how function pointers can
help in this case. ALLOC_OBJECT() is the problem, as it's generic. No
use of making it a "member function" by a function pointer as there is
nothing object-specifc going on, only identifiers/types change.

Can you perhaps bring an example?

http://libtomcrypt.com

That entire library is pluggable. For instance, my HMAC message
authentication code ... er source code [hehehe] doesn't know what hash
you are using. Doesn't know how the hash even works. Just knows it
can call certain functions to handle the message being hashed.

Similarly my Public Key code has no clue how your bignum math is done
[I support three different math libraries, each with their own bignum
data types, init/deinit functions, etc].

etc, etc, etc.

You're just giving up too easily.

Tom

This is all elementary run-time polymorphism. This case is a little bit
different and I'm afraid you fail to see the point. Please look at the
initial post and give your own function version of OBJECT_ALLOCATE()
that works both with text_objects and media_objects if you want to help.
 
T

Tom St Denis

This is all elementary run-time polymorphism. This case is a little bit
different and I'm afraid you fail to see the point. Please look at the
initial post and give your own function version of OBJECT_ALLOCATE()
that works both with text_objects and media_objects if you want to help.

Are you saying you're adding methods that aren't known at compile time?

Then just do variadic definitions and lookup by name...

func_int_t insert_int = myplugin->lookup(methodname);
insert_int(ID, blah, blah, blah);

I don't care to look at your example because I know that you can
emulate all basic functions of C++ with C structures.

You just have to rethink the approach. It won't always be as nice as
C++ [that's why they invented C++] but you can get the same job done
regardless.

Tom
 
G

guillemort

Tom said:
I don't care to look at your example because I know that you can
emulate all basic functions of C++ with C structures.

Tom, why did you answer if you have nothing to say? I'll open another
thread, please don't answer there.
 
T

Thomas J. Gritzan

We have a system of small objects in the lines of

typedef struct {
...
} text_object;

And we have a "object master" that serves as a container for
several of these small objects, in the lines of

typedef struct {
...
text_object *text;
media_object *media;
...
} object_master;

Now, we need a generic/polymorphic way to initialise the small objects
as the initialisation is similar for all of them. I would like to use
inlined functions instead of macros for that, but currently don't see
any natural, clean way for it. A pseudo-implementation with a macro
(ALLOC_OBJ) is provided below.

You want templates for this, don't you? In C, you could use a structure,
that contains the common parts:

/* "common" or "base" object */
typedef struct
{
void* module;
} gen_object;

/* specific object */
typedef struct
{
void* module;
void* media_data;
} media_object;

my_error alloc_media(object_master *om, params* pms)
{
my_errcode err = 0;
media_object *mo;
/* ALLOC_OBJ(om, to, textobj, MEDIA_OBJECT_TYPE, pms); */
alloc_obj((gen_object**)&mo, sizeof *mo, MEDIA_OBJECT_TYPE, pms);
om->media = mo;

return err;
}

void alloc_obj(gen_object** obj, size_t size, int type, params* pms)
{
/* no error checking done */
*obj = my_malloc(size);

my_memset(*obj, 0, size);

(*obj)->module = my_module_find(params);

/* ... */
}
 
G

guillemort

Thomas said:
You want templates for this, don't you?

Exactly. I should have explicitly pointed that out.
In C, you could use a structure,
that contains the common parts:

Well, yes, but it is a little fragile. It seems it is safest to just
stick to the macro. Looks like templates are best emulated with macros,
even though macros are infamous. Like goto that is notoriously
ill-famed, but still useful for try/catch-like error handling. Any
objections?

Thanks for the example, it looks nice and does exactly what I wanted.
However, it seems the macro provides better compile-time safety as no
casts are involved and pointers retain their "identity". I hoped that a
function can be generically used without loosing pointer identity (i.e.
compiler knows the pointer type and hence the pointed-to struct size),
postponing the obvious though in the back of my mind --- it is
impossible. Well, it is impossible and I should not have started this
discussion at all.
 
B

Bill Pursell

Exactly. I should have explicitly pointed that out.


Well, yes, but it is a little fragile. It seems it is safest to just
stick to the macro. Looks like templates are best emulated with macros,
even though macros are infamous. Like goto that is notoriously
ill-famed, but still useful for try/catch-like error handling. Any
objections?

Thanks for the example, it looks nice and does exactly what I wanted.
However, it seems the macro provides better compile-time safety as no
casts are involved and pointers retain their "identity". I hoped that a
function can be generically used without loosing pointer identity (i.e.
compiler knows the pointer type and hence the pointed-to struct size),
postponing the obvious though in the back of my mind --- it is
impossible. Well, it is impossible and I should not have started this
discussion at all.

NO! You absolutely should have started the discussion. I enjoyed
reading the examples, so even if you didn't get exactly what you
wanted, me (and undoubtedly other thread lurkers) reaped some
benefit.

I was recently playing around with some similar ideas and
had hoped that typedef'ing the void * would generate a
compiler warning, but was unable to coerce any warnings. eg

typedef void * foo;
typedef void * bar;

void f(foo a) {return;}
void g(bar a) {return;}

int main(void)
{
foo a;
bar b;
f(b);
g(a);
return 0;
}

Does anyone know a compiler that will warn here? Better
yet, can I coerce a warning out of gcc?
 
D

Dave Thompson

On 21 Jul 2006 22:42:50 -0700, "Bill Pursell" <[email protected]>
wrote:
I was recently playing around with some similar ideas and
had hoped that typedef'ing the void * would generate a
compiler warning, but was unable to coerce any warnings. eg

Nope. typedef in C is strictly an alias, not a distinct type.

The only way to create a distinct new type in C is as a struct or
union, or which the former is more familiar and usually more useful.
(Chris Torek likes to retroform 'struct' as a humorous acronym for
'STRange spelling for User Constructed Type'.)

<OT> In C++ enum types are also distinct, although they still silently
convert to but not from integers; and all class/struct, union, and
enum tags are automatically usable as typenames without being
explicitly declared as typedefs; but typedefs are still aliases.

And of course class/struct types already have true, builtin,
hierarchical polymorphism. If that's what you want. </>

- David.Thompson1 at worldnet.att.net
 
K

Keith Thompson

Dave Thompson said:
On 21 Jul 2006 22:42:50 -0700, "Bill Pursell" <[email protected]>
wrote:


Nope. typedef in C is strictly an alias, not a distinct type.

The only way to create a distinct new type in C is as a struct or
union, or which the former is more familiar and usually more useful.
(Chris Torek likes to retroform 'struct' as a humorous acronym for
'STRange spelling for User Constructed Type'.)

<OT> In C++ enum types are also distinct, although they still silently
convert to but not from integers; and all class/struct, union, and
enum tags are automatically usable as typenames without being
explicitly declared as typedefs; but typedefs are still aliases.

And of course class/struct types already have true, builtin,
hierarchical polymorphism. If that's what you want. </>

Actually, enumerated types are distinct in C as well -- but the
enumeration constants are of type int, not of the enum type. For
example:

enum foo { zero, one, two };
enum foo *fp;
int *ip;
ip = fp; /* illegal */

For that matter, a declaration of a pointer, array, or function type
creates a distinct new type. (The language has no facility for
declaring new numeric types.) But structures are the most flexible
form of new types, since they can contain whatever else you need them
to.
 
I

Ian Collins

Keith said:
Actually, enumerated types are distinct in C as well -- but the
enumeration constants are of type int, not of the enum type. For
example:

enum foo { zero, one, two };
enum foo *fp;
int *ip;
ip = fp; /* illegal */
But they are effectively a type in name only in C, by virtue of

enum foo f;

f = 42;
 
K

Keith Thompson

Ian Collins said:
But they are effectively a type in name only in C, by virtue of

enum foo f;

f = 42;

That's merely because there's an implicit conversion between int and
enum foo. C is full of implicit conversions: between void* and any
pointer-to-object type, between any two numeric types, from a null
pointer constant to any pointer type.

A *value* of type enum foo is pretty much interchangeable with any
integer type. An *object* of type enum foo is not. In that sense,
it's a distinct type.
 
I

Ian Collins

Keith said:
Ian Collins said:
Keith said:
Dave Thompson <[email protected]> writes:
[...]
Actually, enumerated types are distinct in C as well -- but the
enumeration constants are of type int, not of the enum type. For
example:

enum foo { zero, one, two };
enum foo *fp;
int *ip;
ip = fp; /* illegal */

But they are effectively a type in name only in C, by virtue of

enum foo f;

f = 42;


That's merely because there's an implicit conversion between int and
enum foo. C is full of implicit conversions: between void* and any
pointer-to-object type, between any two numeric types, from a null
pointer constant to any pointer type.

A *value* of type enum foo is pretty much interchangeable with any
integer type. An *object* of type enum foo is not. In that sense,
it's a distinct type.
Can you flesh out "An *object* of type enum foo is not" with an example?
I'm not quite sure of the context.
 
B

Ben Pfaff

Ian Collins said:
Can you flesh out "An *object* of type enum foo is not" with an example?
I'm not quite sure of the context.

If we have:
enum foo { zero, one, two } enum_object;
int int_object;
then &object is not compatible with &int_object. In fact,
enum_object and int_object might not have the same size.
 

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,743
Messages
2,569,478
Members
44,899
Latest member
RodneyMcAu

Latest Threads

Top