Allegro.cc - Online Community

Allegro.cc Forums » Off-Topic Ordeals » 15+ years after learning C/C++, I finally learned what a typedef is for

This thread is locked; no one can reply to it. rss feed Print
 1   2 
15+ years after learning C/C++, I finally learned what a typedef is for
Chris Katko
Member #1,881
January 2002
avatar

Lul.

typedef is an alias of a type (same data, but different name), but, the compiler is informed that the typedef alias is to be treated as if it's a CUSTOM/UNIQUE type. So:

#SelectExpand
1typedef float distance; 2 3function(distance d) 4 { 5 //do stuff 6 } 7 8test() 9 { 10 float x = 0; 11 function(x); // FAILS. there is no signature function(float), only function(distance) 12 13 distance y = 0.123; 14 function(y); // works. 15 }

And that's pretty useful in many areas because you may use floats for everything, but many times those floats are NOT the same thing. "Distance" should never be added to (or confused with) "Time".

While that's not anywhere near proper Units of Measurement support, it's sure a step in the right direction being able to use the functionally same data (float), but to mean things that should never be confused.

Imagine

typedef float distance; 

my_funct(float x, float y, distance d)
   {
   }

Without that, it's possible you can pass x, y, and d out of order and it would pass all static checks and compile. And probably be a hard-to-track bug. Oops!

Allegro library uses them for various things so while many functions may parse a value as an integer / bitmask, you should never pass an ALLEGRO_DISPLAY_CONFIG_THINGY to an ALLEGRO_FLAG_THINGY (a different Allegro function's special flags) even if they both resolve down to an integer internally.

On the otherhand, typedef struct is some bullcrap that C uses that nobody else needs. Because if you don't have it in C, you have to write:

void funct(struct [my_struct_type] [structname])

every time instead of just

void funct([my_struct_type] [structname])

::)

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

Audric
Member #907
January 2001

Sorry but I disagree, there is no strict checking.
I just tested with a random online compiler, and then GCC.
As long a two typedefs resolve into the same base type/structure, you can use them interchangeably, with no compiler errors or warnings.

It still is useful for self-documenting though.

You don't have the issue with complex structures, because you never get a case where two different structs have exactly the same NAMES and types of fields.

I made a further test, in C++ :

typedef float distance;
typedef float length;

// overload that takes a distance as parameter
void f(distance d)
  {
  }
// overload that takes a length as parameter
void f(length d)
  {
  }

test.cpp: In function 'void f(length)':
test.cpp:9:6: error: redefinition of 'void f(length)'
test.cpp:5:6: error: 'void f(distance)' previously defined here

Chris Katko
Member #1,881
January 2002
avatar

Really? Interesting... That doc I read must be wrong then. I'll need to review when I get back home.

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

Mark Oates
Member #1,146
March 2001
avatar

I never quite fully understood how typedefs resolve or are automatically casted (or not casted) to the proper types. That is, I feel like a function with a typedef'd parameter would only work if the fundamental type is casted before passing the argument:

typedef float distance;

float twice_the_distance(distance d) { return d * 2; }

void main (int argc, char** argv)
{
   twice_the_distance(3.0); // would expect a compile error
   twice_the_distance(distance(3.0)); // a more proper usage
}

There are other rules similar to this that I never quite got the full detailed picture of rules.

On a similar note, brace initialization has a handy way of handling that when working with classes.

#SelectExpand
1#include <iostream> 2 3class Distance 4{ 5private: 6 float distance; 7public: 8 Distance(float distance) : distance(distance) {} 9 float operator*(float multiplier) { return distance * multiplier; } 10}; 11 12float twice_the_distance(Distance distance) { return distance * 2; } 13 14int main (int argc, char** argv) 15{ 16 twice_the_distance({3.0}); // automatically infers the type and constructor. -std=gnu++11 17 twice_the_distance(3.0); // can also be done without braces (which I find annoying) 18 return 0; 19}

Chris Katko
Member #1,881
January 2002
avatar

I'm even more confused with the {15} stuff. C++ has gone insane. There's too much bloody stuff to remember at the same time. That's a hallmark of a bad language (or any system).

Meanwhile, if you want a structure to "act like" another variable? It's just this:

#SelectExpand
1struct super_float 2 { 3 float data; 4 alias this=data; //<--- treat all uses of a superfloat as if it was superfloat.data (and if that fails, then try superfloat.whatever() as opposed to superfloat.data.whatever()) 5 6 void extra(); 7 void helper_functions(); 8 } 9 10int main() 11 { 12 super_float x = 0; 13 float y = x; 14 15 x.extra(); 16 }

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

relpatseht
Member #5,034
September 2004
avatar

You could always mark your constructor "explicit".

In D, typedefs create a true new type. C/C++ they're just an alias. Useful with templated classes though, which tend to get a bit long.

Neil Roy
Member #2,229
April 2002
avatar

Audric said:

test.cpp: In function 'void f(length)':
test.cpp:9:6: error: redefinition of 'void f(length)'
test.cpp:5:6: error: 'void f(distance)' previously defined here

The typedef's would be resolved by the compiler to floats. So replace your typedef's with float, and you will see the problem. In that case, I would use two structs instead, I don't imagine that would cause a problem with overloading (but then, I normally code only in C).

typedef is mainly meant to make your source code more understandable, but doesn't have any real meaning to the compiler. In this example, the compiler sees both as the same type, and so flags this as an error.

---
“I love you too.” - last words of Wanda Roy

bamccaig
Member #7,536
July 2006
avatar

It all sounded really wonderful at least. :( Alas, the more experienced you are with C++ the less you know about it. :D

Neil Roy
Member #2,229
April 2002
avatar

There's a good quote on Wikipedia about this...

Wikipedia said:

Kernigan and Ritchie state in their book The C Programming Language two reasons for using a typedef. First, it provides a means to make a program more portable or easier to maintain. Instead of having to change a type everywhere it appears throughout the program's source files, only a single typedef statement needs to be changed. Second, a typedef can make a complex definition or declaration easier to understand.

Some people[who?] are opposed to the extensive use of typedefs. Most arguments center on the idea that typedefs simply hide the actual data type of a variable. For example, Greg Kroah-Hartman, a Linux kernel hacker and documenter, discourages their use for anything except function prototype declarations. He argues that this practice not only unnecessarily obfuscates code, it can also cause programmers to accidentally misuse large structures thinking them to be simple types.

---
“I love you too.” - last words of Wanda Roy

bamccaig
Member #7,536
July 2006
avatar

It definitely comes in handy for long or complex types that are going to be used regularly (i.e., function signatures or maybe templates). It's slightly alarming that it took the OP 15+ years to learn what they were though. :P I guess you never really got into function pointers then! Or else you'd be living in Hell doing it. :P

#SelectExpand
1void list_destroy(LINKED_LIST ** plist, FUNCTION_DESTROY data_free) { 2 fprintf(stderr, "%p destroy :t list :f '#func<%p>\n", 3 *plist, data_free); 4 5 while(*plist) { 6 LINKED_LIST * list = *plist; 7 LINKED_LIST * next = list->next; 8 9 if(data_free != NULL && list->data != NULL) { 10 data_free(&list->data); 11 } 12 13 fprintf(stderr, "%p free :t list\n", list); 14 15 free(list); 16 *plist = next; 17 } 18}

The typedef for FUNCTION_DESTROY is fairly simple, but nevertheless function signatures are one of those less commonly encountered beasties that are best localized:

typedef void (*FUNCTION_DESTROY)(void **);

Without the typedef the signature would look like this:

-void list_destroy(LINKED_LIST ** plist, FUNCTION_DESTROY data_free) {
+void list_destroy(LINKED_LIST ** plist, void (*data_free)(void **)) {

Eazy peasy. It really isn't very difficult to understand once you've grasped it, but it's a lot easier to type a \w+ name, and it's faster to read it and comprehend it too I think. :)

Pointer-to-a-pointer because this allows the "free" or "destroy" function to set the outer variable to NULL so that you don't end up dereferencing the wrong memory... It has a negligible cost, but safety over speed IMO unless you can prove that speed is more important right here.

relpatseht
Member #5,034
September 2004
avatar

struct super_float {
float data;
alias this=data; //<--- treat all uses of a superfloat as if it was superfloat.data (and if that fails, then try superfloat.whatever() as opposed to superfloat.data.whatever())
void extra();
void helper_functions();
}

What language is that? I don't think you're reading a C++ book...

Chris Katko
Member #1,881
January 2002
avatar

Somehow I missed writing the phrase "In D".

bamccaig said:

I guess you never really got into function pointers then! Or else you'd be living in Hell doing it. :P

Professionally, I only have to use C#. And it's like... Mana from Heaven compared to C++.

I took C++ courses in college, but again, 99% of my code was my own. We never covered typedefs.

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

l j
Member #10,584
January 2009
avatar

some_type {5} stuff is just an alternate way to call a constructor. It was added to make initialization more uniform. you can construct primitives, arrays and also objects with similar syntax. I don't think anyone uses it for primitives though. Using the special initializer_list type you can create your own constructors for your classes that accept arbitrarily sized lists like in an array definition.

int a{6};
int b[] { 1, 2, 3 };
std::vector<int>c { 1, 2, 3 };

typedef is nothing more than a type alias. Any other features it has are compiler features.

using can also be used in C++ for type aliasing, it also supports templates which typedef doesn't. Its syntax is arguably easier to understand.

If you want something like units you'll have to use classes and operator overloading so you could make a multiplication of time and velocity return a distance object. This way it's also easy to make it impossible to do things like adding time to distance as the compiler would fail to find the required operator for adding a time to distance.

Mark Oates
Member #1,146
March 2001
avatar

taron  said:

typedef is nothing more than a type alias. Any other features it has are compiler features.

Oh nice! That's a good way to look at it.

Quote:

If you want something like units you'll have to use classes and operator overloading so you could make a multiplication of time and velocity return a distance object. This way it's also easy to make it impossible to do things like adding time to distance as the compiler would fail to find the required operator for adding a time to distance.

Nice clarity :)

jmasterx
Member #11,410
October 2009

What's the difference between typedef and #define

I mean, I know one is a preprocessor and the other is compiled but really what difference does it offer?

Neil Roy
Member #2,229
April 2002
avatar

jmasterx said:

What's the difference between typedef and #define

typedef gives symbolic names to types, where as #define can be used to give names to values as well.

So with #define you can have #define TRUE 1 for example.

As and stated already, #define is handled by the preprocessor, and typedef by the compiler.

With #define the preprocessor just straight out swaps the name for whatever argument you provided, in the above example, TRUE will be replaced with 1 without question. typedef is a c command and so is handled by the compiler.

Many people prefer that you use const rather than #define. Personally, I only ever use typedef with struct in C. Otherwise, I prefer to use the actual types to avoid confusion.

#define is used most often to avoid magic numbers. OpenGL for example uses them to define things like GL_TRUE, this way your code is more portable. OpenGL uses typedef for things like typedef unsigned int GLuint; so you can use GLuint in your code and it is portable. If on another platform this changes for some odd reason, your code is still okay, the typedef will reflect the platform you are compiling on which is nice.

An example OpenGL using a typedef and a #define in the same line...
GLboolean myBool = GL_TRUE;

---
“I love you too.” - last words of Wanda Roy

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Kitty Cat
Member #2,815
October 2002
avatar

In C++ it's recommended to use using to create a type alias:

using distance = float;
// equivalent to
typedef float distance;

Unlike a typedef though, it works with unresolved templates:

template<typename T>
using ShortName = some::stupid::long::namespace::thanks::boost<T>;
...
ShortName<MyType>; // now resolves the template with MyType

I believe I heard that a future C++ version may be adding an option for strong aliases, which are treated as new types that don't automatically resolve/cast to the base type but otherwise have the same behavior as the base type.

--
"Do not meddle in the affairs of cats, for they are subtle and will pee on your computer." -- Bruce Graham

Chris Katko
Member #1,881
January 2002
avatar

#define MACRO RootOfAllEvil()

Yeah. Macros are basically just "I read your text file, I see SYMBOL (some text), I replace them with text." It's that dumb. It means the code you look at in your editor, will not look like that when it gets to the compiler. And, remember, it doesn't know what the macro is. If it changes 1000 lines of code (many of them different), the compiler has no idea they're all related to the same change.

The reason const/static (<--abused to hell in D)/and other qualifiers are BETTER is because it's telling the COMPILER what you want. Even if it's on a practical level, the compiler can then apply both optizations, as well as checks, that a text-level change won't. (Ever ask how to regex parse HTML/XML and everyone will scream at you for using TEXT parsing to parse a tree?)

If you're going to use macros, you might as well go balls-out and make your own linter / pre-processor stage that does exactly what you want. Or, go the smarter route, and parse the AST (Abstract Syntax Tree) itself and do your modifications there. Which means you can treat your code like a hierarchy like files and directories, move them around, modify them, and recognize more complex patterns.

(D!) Templates are (almost always!) better than macros because they work on the representation of your code (is it a int? is it a class with a taco() method? is it a function pointer to a resource to a ...) and not mere text. All of those examples I mentioned are super easy to process. I can (and do below) process "does ANY argument in my function, match X type, and if so, what position". Having a macro "not care" about position while still caring about the outer context (is this the right object?), would be more difficult, IIRC.

What I've been doing with templates (and I'm NOT an expert) in D (because they're so easy and intuitive compared to C++ template) would be impossible for me in any other language without writing my own high-level object-oriented parsing stage. I'm turning this:

al_draw("tacos!", pos(25,25), color(255,0,0), scale(2.0f)); //order doesn't matter! arguments are grouped! I can send a BITMAP or TEXT with the same commands!

Into this

#SelectExpand
1al_draw_text(font, ...); 2 3// or 4 5al_draw_bitmap 6 7// or 8al_draw_scaled_bitmap(ALLEGRO_BITMAP *bitmap, 9 float sx, float sy, float sw, float sh, 10 float dx, float dy, float dw, float dh, int flags) //no scalex, scaley! 11 12// or 13al_draw_scaled_rotated_bitmap(ALLEGRO_BITMAP *bitmap, 14 float cx, float cy, float dx, float dy, float xscale, float yscale, 15 float angle, int flags) //there IS a scalex, scaley!? So many arguments!!!

And it all happens at COMPILE time, with type checking and all the protections of normal code, with zero run-time cost. My before-transformed code is checked AND the after-transformed code is checked, and I get informative error messages if either one doesn't make sense! It also compiles in 2 seconds on my netbook with a Celeron processor and 2 GB of RAM. (Because D supports modules instead of 1970's era include guards. ::) So it never includes the same thing twice, and forward-declarations are never required.)

Now, like I said, macros "can" be used appropriately. Use the right tool for the right job. But the fact most languages don't even allow macros anymore (but still allow GOTO!!!) should be a clue that they're often more danger than worth.

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

Neil Roy
Member #2,229
April 2002
avatar

Kitty Cat said:

Unlike a typedef though, it works with unresolved templates:

Oooh, I like that! I am a C programmer and one of the biggest turn-offs for me for C++ is those horribly long names.

---
“I love you too.” - last words of Wanda Roy

bamccaig
Member #7,536
July 2006
avatar

Step into the 21st century, Neil! Programming languages can do so much more. ;) OK, full disclosure, Lisp was doing way more cool things in the 80's, if not the 50's. :P

Neil Roy
Member #2,229
April 2002
avatar

bamccaig said:

Programming languages can do so much more

What can they do that I cannot do in C? And I am talking about the end result here. I can do what they can do in C, and I can do it faster with less convoluted code.

I wanted a stack style vector, I wrote my own in C. Worked like a charm and isn't an ugly mess to work with like C++.

I got a laugh out of some recent C++ code I seen where someone was using a string type, then they pass a .c_str() to the function. I was like, why not just use a bloody `char *`?! It was absolutely ridiculous some of the hoops people will jump through to use C++ code where it just isn't necessary.

Lots of libraries that run modern AAA titles like OpenGL and SDL2 as well as most of Allegro (though I guess it has a mix, which I think is foolish) are written in C.

I think the fact that C hasn't vanished after all this time says something for it. It's still the most portable language there is, and ports easily to other languages. Where as C++ can have trouble in that regard.

I have created games with it, I have programmed 3D with it with just straight windows code + OpenGL etc... I am in the 21st century with a language that has stood the test of time. Remember, the Titanic was new and modern, as was all her sister ships. ;)

---
“I love you too.” - last words of Wanda Roy

Eric Johnson
Member #14,841
January 2013
avatar

Neil Roy said:

I wanted a stack style vector, I wrote my own in C. Worked like a charm and isn't an ugly mess to work with like C++.

What is it about C++ vectors that are "an ugly mess"? I've never had troubles with them.

Kitty Cat
Member #2,815
October 2002
avatar

Neil Roy said:

What can they do that I cannot do in C? And I am talking about the end result here.

End result? Nothing. Just like there's nothing in C that you can't do in assembly. However, C++ abstracts certain things to make it easier and less error prone to do compared to C. Things like RAII and smart pointers help make memory management automatic, but it doesn't accomplish anything you can't do by hand in C or assembly (but by being automatic, it's not possible to forget to clean up or to clean up too early, and a compiler can better reason about certain program logic to warn you about problems early, or perform more aggressive optimizations that create the same intended results).

--
"Do not meddle in the affairs of cats, for they are subtle and will pee on your computer." -- Bruce Graham

Chris Katko
Member #1,881
January 2002
avatar

Neil Roy said:

What can they do that I cannot do in C? And I am talking about the end result here. I can do what they can do in C, and I can do it faster with less convoluted code.

Then why not use assembler?

D can literally compile down to C code (-BetterC option) and gives you SUPER easy templates. (I'm not saying only use D, but there are good higher level languages these days than the walking abortion that was Java.)

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

 1   2 


Go to: