Getters and setters in pure C?
Asked Answered
C

3

15

Can I use getters and setters in pure C instead of using extern variables?

Centrist answered 5/12, 2014 at 12:47 Comment(0)
D
31

First of all, don't listen to anyone saying "there is no object-orientation in language x" because they have truly not understood that OO is a program design method, completely apart from language syntax.

Some languages have elegant ways to implement OO, some have not. Yet it is possible to write an object-oriented program in any language, for example in C. Similarly, your program will not automagically get a proper OO design just because you wrote it in Java, or because you used certain language keywords.

The way you implement private encapsulation in C is a bit more crude than in languages with OO support, but it does like this:

// module.h

void set_x (int n);
int get_x (void);

// module.c

static int x; // private variable

void set_x (int n)
{
  x = n;
}

int get_x (void)
{
  return x;
}

// main.c

#include "module.h"

int main (void)
{
  set_x(5);
  printf("%d", get_x());
}

Can call it "class" or "ADT" or "code module" as you prefer.

This is how every reasonable C program out there is written. And has been written for the past 30-40 years or so, as long as program design has existed. If you say there are no setters/getters in a C program, then that is because you have no experience of using C.

EDIT: For completeness, the thread-safe, multi-instance version of the above is also possible using "opaque type", see How to do private encapsulation in C?

Dravidian answered 5/12, 2014 at 13:9 Comment(7)
Yes, this is exactly what I try to understand: do these approaches (static variable+'C-style getter' and simple 'variable+extern declaration') absolutely replacable, or one is still preferable?Centrist
@giorgim Same, though if the variable type must be exposed to the caller, you have to implement that through incomplete ("opaque") type, as mentioned in another answer.Dravidian
@giorgim Here is an example and here another one.Dravidian
@giorgim Formally the term is incomplete type, which basically means "here is a type, I'll explain elsewhere how it is defined". Inside the h file, the type definition is unknown, and it is also unknown to the caller. Still, you can use a pointer to this type without knowing the contents. You just can't declare a variable of the type nor access the contents. (Works exactly like an abstract base class, if you are familiar with C++.)Dravidian
@giorgim As shown in the examples linked, simply declare a struct in the h file without a definition: typedef struct engine_t engine_t;, and then only define it in the c file. That way the caller will have access to the struct declaration (type name) but not the definition (contents).Dravidian
@giorgim It is sufficient just to type struct engine_t; to get an incomplete type. The typedef is there so you don't have to type out struct all over the place, which is particularly confusing to the caller when you use opaque type and there is no struct definition to be found.Dravidian
simply awesome.Supplejack
D
15

Yes, it's very much possible and sometimes even useful. C supports opaque types:

struct Context;

C code compiled with only this declaration in scope can not access any hypothetical members of the struct, and can't use value of type Context either. But it can still handle pointers to Context values, so functions like these are possible:

Context *make_context(...);
int context_get_foo(Context *);
void context_set_foo(Context *, int);

This pattern insulates the client C code from any changes to the size or internal layout of Context. Note that this is a stronger guarantee than simply declaring but not documenting the members: Even if the programmers duly ignore the undocumented members, by-value use of the struct is permitted (and will certainly slip in), and now the code has to be recompiled when the size changes. In other words, opaque types only handled through pointers give greater ABI stability.

Dulcie answered 5/12, 2014 at 13:7 Comment(6)
Opaque type is a great way to implement true private encapsulation of structs and it is definitely the way to go for professional C programmers. But opaque type isn't necessary just to declare some private variables and implement setters/getters for them, see my answer for a simplified way to do so.Dravidian
@Dravidian What you describe also works, is indeed simpler, and may even be closer to what OP asked for (given that they mentioned extern). But I wouldn't call it a "simplified way" because it's a quite different tool, in the same way static members in Java are not "simplified" instance members.Dulcie
Is it reasonable to say now that IF I ABSOLUTELY don't need opaque structs in a project, should I use 'static variables+getters' to have a profeccional style, or it is indifferent whether to use this or just direct access from other modules by 'extern declaration'?Centrist
@Centrist You should use static + setter/getters. extern global variables is very bad practice and considered "spaghetti coding". There is in fact never any reason to use the extern keyword in C.Dravidian
I "feel" that extern-style is lame and static+getters is right one, but I wished to prove it with live examples - why extern is bad. The reason is that we start to develop new project on a base of some groundwork that is not always a model code. Need to decide whether to spend time on rewriting tons of code into static-style or leave extern-style to save time.Centrist
@Centrist Unless the project is a complete mess, it is probably best to leave it as it is. Just make sure that all new code you produce don't contain such global variables.Dravidian
F
1

Another approach is by using a global variable and inline functions:

// module.h

inline void set_x (int n) {extern int x; x = n;}
inline int get_x (void) {extern int x; return x;}

// module.c

int x; // global variable

// main.c

#include "module.h"

int main (void)
{
  set_x(5);
  printf("%d", get_x());
}

It has two advantages:

  1. Getters and setters become easily inlineable

  2. It becomes clear to the compiler that getters have no side effects, which allows further optimizations and produces no warnings in cases like this one:

// warning: compound statement with side effects

if(get_x() || get_y())

Of course, a "dedicated" (read: dumb) programmer can always write extern int x; in their code and use the variable directly. On the other hand, a "dedicated" programmer can also easily remove the static keyword and use it anyway...

Frons answered 25/5, 2022 at 9:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.