Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I hide some fields of struct in C?

Tags:

c

typedef

I'm trying to implement a struct person, and I need to hide some fields or make them constant. A trick for create private fields.

Header:

#pragma once

#define NAME_MAX_LEN 20

typedef struct _person {
    float wage;
    int groupid;
} Person;

const char const *getName (Person *p);
int getId (Person *p);

/// OTHER FUNCTIONS

Source

#include "person.h"


struct _person
{
    int id;

    float wage;
    int groupid;

    char name[NAME_MAX_LEN];
};

/// FUNCTIONS

GCC says that person.c:7:8: error: redefinition a 'struct _person' struct _person

I can write this in a header, but after it, I can't use fields of a struct.

typedef struct _person Person;
like image 827
Wootiae Avatar asked Mar 22 '19 20:03

Wootiae


People also ask

Can struct have private members in C?

In the C++ language, a struct is identical to a C++ class but has a different default visibility: class members are private by default, whereas struct members are public by default.

Can a struct have itself as a member?

Although a structure cannot contain an instance of its own type, it can can contain a pointer to another structure of its own type, or even to itself.


2 Answers

A struct cannot have multiple conflicting definitions. As such, you can't create a struct that hides some of the fields.

What you can do however it declare that the struct exists in the header without defining it. Then the caller is restricted to using only a pointer to the struct and using functions in your implementation to modify it.

For example, you could define your header as follows:

typedef struct _person Person;

Person *init(const char *name, int id, float wage, int groupid);

const char *getName (const Person *p);
int getId (const Person *p);
float getWage (const Person *p);
int getGroupid (const Person *p);

And your implementation would contain:

#include "person.h"

struct _person
{
    int id;

    float wage;
    int groupid;

    char name[NAME_MAX_LEN];
};

Person *init(const char *name, int id, float wage, int groupid)
{
    Person *p = malloc(sizeof *p);
    strcpy(p->name, name);
    p->id = id;
    p->wage= wage;
    p->groupid= groupid;
    return p;
}

...
like image 66
dbush Avatar answered Sep 18 '22 15:09

dbush


C has no mechanism for hiding individual members of a structure type. However, by operating only in terms of pointers to such a type, and not providing a definition, you can make the whole type opaque. Users would then have to use the functions you provide to manipulate instances in any way. This is a thing that is sometimes done.

To some extent, you may be able to achieve something like what you describe with a hidden context. For example, consider this:

header.h

typedef struct _person {
    float wage;
    int groupid;
} Person;

implementation.c

struct _person_real {
    Person person;  // must be first, and is a structure, not a pointer.
    int id;
    char name[NAME_MAX_LEN];
};

Now you can do this:

Person *create_person(char name[]) {
    struct _person_real *pr = malloc(sizeof(*pr));

    if (pr) {
        pr->person.wage = DEFAULT_WAGE;
        pr->person.groupid = DEFAULT_GROUPID;
        pr->id = generate_id();
        strncpy(pr->name, name, sizeof(pr->name));
        pr->name[sizeof(pr->name) - 1] = '\0';

        return &pr->person;  // <-- NOTE WELL
    } else {
        return NULL;
    }
}

A pointer to the first member of a structure always points also to the whole structure, too, so if the client passes a pointer obtained from that function back to you, you can

struct _person_real *pr = (struct _person_real *) Person_pointer;

and work on the members from the larger context.

Be well aware, however, that such a scheme is risky. Nothing prevents a user from creating a Person without the larger context, and passing a pointer to it to a function that expects the context object to be present. There are other issues.

Overall, C APIs generally either take the opaque structure approach or just carefully document what clients are permitted to do with the data they have access to, or even just document how everything works, so that users can make their own choices. These, especially the latter, are well aligned with overall C approaches and idioms -- C does not hold your hand, or protect you from doing harm. It trusts you to know what you're doing, and to do only what you intend to do.

like image 35
John Bollinger Avatar answered Sep 18 '22 15:09

John Bollinger