If I Do a 'Typedef' in C or C++, When Should I Add '_T' at the End of Typedef'Ed Type

If I do a `typedef` in C or C++, when should I add `_t` at the end of typedef'ed type?

In POSIX, names ending with _t are reserved, so if you are targeting a POSIX system (e.g., Linux), you should not end your types with _t.

What does a type followed by _t (underscore-t) represent?

As Douglas Mayle noted, it basically denotes a type name. Consequently, you would be ill-advised to end variable or function names with '_t' since it could cause some confusion. As well as size_t, the C89 standard defines wchar_t, off_t, ptrdiff_t, and probably some others I've forgotten. The C99 standard defines a lot of extra types, such as uintptr_t, intmax_t, int8_t, uint_least16_t, uint_fast32_t, and so on. These new types are formally defined in <stdint.h> but most often you will use <inttypes.h> which (unusually for standard C headers) includes <stdint.h>. It (<inttypes.h>) also defines macros for use with the printf() and scanf().

As Matt Curtis noted, there is no significance to the compiler in the suffix; it is a human-oriented convention.

However, you should also note that POSIX defines a lot of extra type names ending in '_t', and reserves the suffix for the implementation. That means that if you are working on POSIX-related systems, defining your own type names with the convention is ill-advised. The system I work on has done it (for more than 20 years); we regularly get tripped up by systems defining types with the same name as we define.

Why should we typedef a struct so often in C?

As Greg Hewgill said, the typedef means you no longer have to write struct all over the place. That not only saves keystrokes, it also can make the code cleaner since it provides a smidgen more abstraction.

Stuff like

typedef struct {
int x, y;
} Point;

Point point_new(int x, int y)
{
Point a;
a.x = x;
a.y = y;
return a;
}

becomes cleaner when you don't need to see the "struct" keyword all over the place, it looks more as if there really is a type called "Point" in your language. Which, after the typedef, is the case I guess.

Also note that while your example (and mine) omitted naming the struct itself, actually naming it is also useful for when you want to provide an opaque type. Then you'd have code like this in the header, for instance:

typedef struct Point Point;

Point * point_new(int x, int y);

and then provide the struct definition in the implementation file:

struct Point
{
int x, y;
};

Point * point_new(int x, int y)
{
Point *p;
if((p = malloc(sizeof *p)) != NULL)
{
p->x = x;
p->y = y;
}
return p;
}

In this latter case, you cannot return the Point by value, since its definition is hidden from users of the header file. This is a technique used widely in GTK+, for instance.

UPDATE Note that there are also highly-regarded C projects where this use of typedef to hide struct is considered a bad idea, the Linux kernel is probably the most well-known such project. See Chapter 5 of The Linux Kernel CodingStyle document for Linus' angry words. :) My point is that the "should" in the question is perhaps not set in stone, after all.

typedef struct vs struct definitions

The common idiom is using both:

typedef struct S { 
int x;
} S;

They are different definitions. To make the discussion clearer I will split the sentence:

struct S { 
int x;
};

typedef struct S S;

In the first line you are defining the identifier S within the struct name space (not in the C++ sense). You can use it and define variables or function arguments of the newly defined type by defining the type of the argument as struct S:

void f( struct S argument ); // struct is required here

The second line adds a type alias S in the global name space and thus allows you to just write:

void f( S argument ); // struct keyword no longer needed

Note that since both identifier name spaces are different, defining S both in the structs and global spaces is not an error, as it is not redefining the same identifier, but rather creating a different identifier in a different place.

To make the difference clearer:

typedef struct S { 
int x;
} T;

void S() { } // correct

//void T() {} // error: symbol T already defined as an alias to 'struct S'

You can define a function with the same name of the struct as the identifiers are kept in different spaces, but you cannot define a function with the same name as a typedef as those identifiers collide.

In C++, it is slightly different as the rules to locate a symbol have changed subtly. C++ still keeps the two different identifier spaces, but unlike in C, when you only define the symbol within the class identifier space, you are not required to provide the struct/class keyword:

 // C++
struct S {
int x;
}; // S defined as a class

void f( S a ); // correct: struct is optional

What changes are the search rules, not where the identifiers are defined. The compiler will search the global identifier table and after S has not been found it will search for S within the class identifiers.

The code presented before behaves in the same way:

typedef struct S { 
int x;
} T;

void S() {} // correct [*]

//void T() {} // error: symbol T already defined as an alias to 'struct S'

After the definition of the S function in the second line, the struct S cannot be resolved automatically by the compiler, and to create an object or define an argument of that type you must fall back to including the struct keyword:

// previous code here...
int main() {
S();
struct S s;
}

When should I use typedef in C++?

Template Metaprogramming

typedef is necessary for many template metaprogramming tasks -- whenever a class is treated as a "compile-time type function", a typedef is used as a "compile-time type value" to obtain the resulting type. E.g. consider a simple metafunction for converting a pointer type to its base type:

template<typename T>
struct strip_pointer_from;

template<typename T>
struct strip_pointer_from<T*> { // Partial specialisation for pointer types
typedef T type;
};

Example: the type expression strip_pointer_from<double*>::type evaluates to double. Note that template metaprogramming is not commonly used outside of library development.

Simplifying Function Pointer Types

typedef is helpful for giving a short, sharp alias to complicated function pointer types:

typedef int (*my_callback_function_type)(int, double, std::string);

void RegisterCallback(my_callback_function_type fn) {
...
}

How to properly use `typedef` for structs in C?

What this does:

typedef struct {
int a;
int b;
} ab_t;

Is define an anonymous struct and give it the alias ab_t. For this case there's no problem as you can always use the alias. It would however be a problem if one of the members was a pointer to this type.

If for example you tried to do something like this:

typedef struct {
int count;
TNODE *left, *right;
} TNODE;

This wouldn't work because the type TNODE is not yet defined at the point it is used, and you can't use the tag of the struct (i.e. the name that comes after the struct keyword) because it doesn't have one.

Called Object Type is Not a Function or Pointer With Typedef and Classes

To answer my own question, I will be repeating and explaining the code I wrote.

We have two classes which are stored in the two files, MQTT.cpp and Client.cpp. MQTT.cpp has the namespace "godot" and the class name "MQTT" (so, godot::MQTT::). Client.cpp has the namespace "libumqtt" and the class name "Client" (so, libumqtt::Client::).

MQTT has a function that is defined as void write_log(String verbosity, Variant message); It also has the function initialize();. The arguments and return type do not matter for initialize. To initialize the client, I create an object of Client and then call its own method which is also called initialize.

// MQTT.cpp
String MQTT::initialize() {
// ...

// This code will initialize the Client class and pass the function `write_log` to the Client object.
// `write_log` is marked as private in the header file MQTT.hpp.
libumqtt::Client client;
client.initialize(*this, &MQTT::write_log); // Initialize Instance of Client

// ...
}

In Client.hpp, I marked initialize as public and added the line void initialize(godot::MQTT& mqtt_class, Logger logger);. I also added the typedef typedef void (godot::MQTT::*Logger)(godot::String, godot::Variant); so I can just refer to write_log's data type as Logger.

To call the write_log function, I used the below code.

// Client.cpp
void Client::initialize(godot::MQTT& mqtt_class, Logger write_log) {
// ...

// Send Test Log
// Why Invoke? - https://isocpp.org/wiki/faq/pointers-to-members#macro-for-ptr-to-memfn
std::invoke(write_log, mqtt_class, "error", "Initializing MQTT Client!!! TypeDEF!!!");

// ...
}

What I was doing wrong was I was trying to call a pointer to a member function without using invoke. Since I am new to cpp, I wouldn't have known about the fact that I cannot just call a pointer directly if it goes to another function that is not a direct part of my class. Using invoke is easier than trying to use ((object).*(ptrToMember)) which has syntax I do not completely understand yet.

What I did was I created a pointer to my class, godot::MQTT, by grabbing the pointer of the keyword this. I then create a reference to the pointer provided by this through the variable godot::MQTT& mqtt_class. After that, calling the function write_log is as simple as providing the reference to the calling class' object and then the pointer to the function in the calling class, as well as the arguments needed to call the function.

As I am a noob to C++, someone with more experience should edit this sentence out and explain why you need both the reference to the calling object on top of the pointer to the function that needs to be called.

Standard for typedef'ing

Much of this comes down to personal preference, with the key being to be consistent (or if you have a company convention, use that). The following article has some naming guides:

http://www.montefiore.ulg.ac.be/~piater/Cours/Coding-Style/

Note that it switches the '_t' portion:

typedef struct node_t {
void *content;
struct node_t *next;
} Node;

typedef enum season_t { SPRING, SUMMER, FALL, WINTER } Season;

There was an earlier discussion on C naming conventions here:

What are the most common naming conventions in C?



Related Topics



Leave a reply



Submit