Home > database >  How do you declare a distinct type in C?
How do you declare a distinct type in C?

Time:10-16

Background

I am working with a collection of opaque types of my own design ("the collection").

At higher levels of my program, I want to pass around handles to each instance of each object having a type in the collection. The lower levels of my program (that know about the details of the type) deal with the underlying structure associated with each type, and apply appropriate operations.

One reason to use this approach—in the case of structs—is (N2176 6.2.5-28):

All pointers to structure types shall have the same representation and alignment requirements as each other.

I want each type to be distinct (there is no inheritance or polymorphism among members of the collection) so that I can take advantage of compile-time detection of type errors.

Headers are similar to

// FILE: value.h
#include <stddef.h>
typedef struct s_myValue myValue;
typedef myValue * myHandle;
typedef const myHandle constMyHandle;
int value_init(myHandle, size_t);
int value_f1(myHandle, ...);
int value_f2(myHandle, ...);

(Credit goes to another SO user for suggesting these kind of typedefs—can't seem to find a better reference just now.)

In one case, I have decided that at the lower level, I am going to demote each myHandle to a void * for internal processing. Therefore (I think), the only reason to have myValue, myHandle, and constMyHandle is to provide for the interface, and it does not matter how I define struct s_myValue.

Except that all members of the collection must be distinct. How to guarantee this?

Prompted by ibid. (6.7.2.3-5):

Each declaration of a structure, union, or enumerated type which does not include a tag declares a distinct type.

and the specification of sytax for declarations (ibid., A.2.2), I have come up with the following minimal (I think) declaration:

Declaration No. 1 (wrong)

// FILE: value.c  
#include "value.h"
// ...
struct s_myValue
{
    _Static_assert ( 1 , "" ) ;
} ;
// ...

But this seems kludgey. Also, not valid, as pointed out by @kamilcuk (N2176 6.2.5-20):

A structure type describes a sequentially allocated nonempty set of member objects... each of which has an optionally specified name and possibly distinct type.

Declaration No. 2

Maybe:

// FILE: value.c  
#include "value.h"
// ...
struct s_myValue
{
    // tag randomly generated (UUID4, reorganized)
    s_myValue * a6e64fd2eb4689eab294b9524e0efa1 ;
} ;
// ...

But, yuck.

The Question

Is there a more expressive, elegant way to declare a struct that is distinct from all other types? Is there a way to declare that is more consistent with the design of the C programming language? (Yes, I realize that I just used the words "design" and "C" in the same sentence.)

I don't think struct s_myValue { } ; is a candidate, because that declaration seems not to be standard.

CodePudding user response:

The following

Each declaration of a structure, union, or enumerated type which does not include a tag declares a distinct type.

says nothing about whether declarations of structure, union, or enumerated types with tags declare unique types.

To paraphrase the standard (specifically C90), I paraphrase because I don't know if I can quote it without violating their copyright.

struct identitifer;

specifies a structure type and declares a tag. It specifies a new type distinct from any type with the same tag.

So all you need is

struct s_myValue {
    //structure member declarations go here.
}

CodePudding user response:

You can use a macro to generate to generate your types and prototypes:

#define value_create(suffix)\
    value_create2(suffix)

#define value_create2(suffix)\
    typedef struct value_##suffix value_##suffix;\
    value_##suffix *value_create_##suffix(value_##suffix *)

value_create(1);
value_create(2);

int main() {
    value_1 *value_create_1();
    value_2 *value_create_2();
}

You would also need to generate a matching implementation:

  1. You can change the macro to also generate the implementation. This means the point of using an opaque pointer goes out the window. See for example CTL, mlib.
  2. You write a different macro that generate the implementation, say, value_create_impl() which you now have to synchronize with use above:
value_create_impl(1);
value_create_impl(2);

To me this would only make sense if there was a difference in the implementation, for instance, you want a container for int and a container for double.

Macros are a blunt and limited tool, and you up with having to change how to call a macro to see if then generate the code you really wanted. My advise is not to down this route, or at least look at existing solutions first to see if you like it.

  • Related