Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

We all have definitely used typedefs and #defines one time or the other. Today while working with them, I started pondering on a thing.

Consider the below 2 situations to use int data type with another name:

typedef int MYINTEGER

and

#define MYINTEGER int

Like above situation, we can, in many situations, very well accomplish a thing using #define, and also do the same using typedef, although the ways in which we do the same may be quite different. #define can also perform MACRO actions which a typedef cannot.

Although the basic reason for using them is the different, how different is their working? When should one be preferred over the other when both can be used? Also, is one guaranteed to be faster than the other in which situations? (e.g. #define is preprocessor directive, so everything is done way earlier than at compiling or runtime).

share|improve this question
8  
Use #define what they are designed for. Conditional compilation based on properties that you can not know when writing the code (like OS/Compiler). Use language constructs otherwise. –  Loki Astari Jan 18 '12 at 20:28
add comment

11 Answers 11

up vote 53 down vote accepted

A typedef is generally preferred unless there's some odd reason that you specifically need a macro.

macros do textual substitution, which can do considerable violence to the semantics of the code. For example, given:

#define MYINTEGER int

you could legally write:

short MYINTEGER x = 42;

because short MYINTEGER expands to short int.

On the other hand, with a typedef:

typedef int MYINTEGER:

the name MYINTEGER is another name for the type int, not a textual substitution for the keyword "int".

Things get even worse with more complicated types. For example, given this:

typedef char *char_ptr;
char_ptr a, b;
#define CHAR_PTR char*
CHAR_PTR c, d;

a, b, and c are all pointers, but d is a char, because the last line expands to:

char* c, d;

which is equivalent to

char *c;
char d;

(Typedefs for pointer types are usually not a good idea, but this illustrates the point.)

Another odd case:

#define DWORD long
DWORD double x;     /* Huh? */
share|improve this answer
    
Pointers to blame again! :) Any other example besides pointers where it is unwise to use such macros? –  c0da Jan 18 '12 at 11:29
2  
Not that I'd ever use a macro in place of a typedef, but the proper way to build a macro that does something with whatever follows is to use arguments: #define CHAR_PTR(x) char *x. This will at least cause the compiler to kvetch if used incorrectly. –  Blrfl Jan 18 '12 at 13:17
1  
Don’t forget: const CHAR_PTR mutable_pointer_to_const_char; const char_ptr const_pointer_to_mutable_char; –  Jon Purdy Jan 18 '12 at 17:16
3  
Defines also make error messages incomprehensible –  Andreas Bonini Jan 18 '12 at 19:41
    
An example of the pain that misuse of macros can cause (though not directly relevant to macros vs. typedefs): github.com/Keith-S-Thompson/42 –  Keith Thompson Jan 19 '12 at 21:47
add comment

By far, the biggest problem with macros is that they are not scoped. That alone warrants the use of typedef. Also, it is more clear semantically. When someone who reads your code sees a define, he won't know what it is about until he reads it and comprehends the entire macro. A typedef tells the reader that a type name will be defined. (I should mention that I'm talking about C++. I'm not sure about typedef scoping for C, but I guess it's similar).

share|improve this answer
    
But as I have shown in the example provided in the question, a typedef and #define use the same number of words! Also, these 3 words of a macro won't cause any confusion, as the words are the same, but just rearranged. :) –  c0da Jan 18 '12 at 11:27
1  
Yes, but it's not about shortness. A macro can do a zillion other things besides typedefing. But personally I think scoping is the most important. –  Tamás Szelei Jan 18 '12 at 11:29
1  
@c0da - you should not be using macros for typedefs, you should be using typedefs for typedefs. The actual effect of the macro can be very different, as illustrated by various responses. –  Joris Timmermans Jan 18 '12 at 11:31
add comment

Source code is mainly written for fellow developers. Computers use a compiled version of it.

In this perspective typedef carries a meaning that #define doesn't.

share|improve this answer
    
+1: It's about the meaning. –  S.Lott Jan 18 '12 at 15:22
add comment

Keith Thompson's answer is very good and together with Tamás Szelei's additional points about scoping should provide all the background you need.

You should always consider macros the very last resort of the desperate. There are certain things you can only do with macros. Even then, you should be thinking long and hard if you really want to do it. The amount of pain in debugging that can be caused by subtly broken macros is considerable, and will have you looking through preprocessed files. It's worth doing that just once, to get a feeling for the scope of the problem in C++ - just the size of the preprocessed file might be an eye-opener.

share|improve this answer
add comment

Use the tool with the least power that gets the job done, and the one with most warnings. #define is evaluated in the preprocessor, you are largely on your own there. typedef is evaluated by the compiler. Checks are given and typedef can only define types, as the name says. So in your example definitely go for typedef.

share|improve this answer
add comment

In addition to all valid remarks about scope and text replacement already given, #define is also not fully compatible with typedef! Namely when it comes to the case of function pointers.

typedef void(*fptr_t)(void);

This declares a type fptr_t which is a pointer to a function of the type void func(void).

You cannot declare this type with a macro. #define fptr_t void(*)(void) will obviously not work. You would have to write something obscure like #define fptr_t(name) void(*name)(void), which really doesn't make any sense in the C language, where we have no constructors.

Array pointers cannot be declared with #define either: typedef int(*arr_ptr)[10];


And while C has no language support for type safety worth mentioning, another case where typedef and #define is not compatible is when you do suspicious type conversions. The compiler and/or astatic analyser tool might be able to give warnings for such conversions if you use typedef.

share|improve this answer
    
Even better are combinations of function and array pointers, like char *(*(*foo())[])(); (foo is a function returning a pointer to an array of pointers to functions returning pointer to char). –  John Bode Feb 6 '12 at 12:31
add comment

Don't forget the implications of this in your debugger. Some debuggers don't handle #define very well. Play around with both in the debugger you use. Remember, you'll spend more time reading it than writing it.

share|improve this answer
add comment

typedef fits the C++ philosophy: all possible checks/asserts in compilation time. #define is just a preprocessor trick which hide a lot of semantic to the compiler. You should not worry about compilation performance more than code correctness.

When you create a new type you are defining a new "thing" that your program's domain manipulates. So you can use this "thing" to compose functions and classes, and you can use the compiler for your benefit to do static checks. Anyway, as C++ is compatible with C, there are a lot of implicit conversion between int and int-based types which do not generate warnings. So you don't get the full power of static checks in this case. However, you could replace your typedef with an enum to find implicit conversions. E.g.: If you've typedef int Age;, then you can replace with enum Age { }; and you will get all kind of errors by implicit conversions between Age and int.

Another thing: the typedef can be inside a namespace.

share|improve this answer
add comment

Using defines instead of typedefs is troubling under another important aspect, and namely the concept of type traits. Consider different classes (think of standard containers) all of which define their specific typedefs. You can write generic code by referring to the typedefs. Examples of this include the general container requirements (c++ standard 23.2.1 [container.requirements.general]) like

X::value_type
X::reference
X::difference_type
X::size_type

All of this is not expressible in a generic way with a macro because it is not scoped.

share|improve this answer
add comment

Beyond the normal arguments against define, how would you write this function using Macros?

template <typename IterType>
typename IterType::value_type Sum(
    const IterType& begin, 
    const IterType& end, 
    const IterType::value_type& initialValue)
{
    typename IterType::value_type result = initialValue;
    for (IterType i = begin; i != end; ++i)
        result += i;

    return result;
}

....

vector<int> values;
int sum = Sum(values.begin(), values.end(), 0);

This is obviously a trivial example, but that function can sum over any forward iterable sequence of a type that implements addition*. Typedefs used like this are an important element of Generic Programming.

*I just wrote this here, I leave compiling it as an excercise for the reader :-)

EDIT:

This answer seems to be causig a lot of confusion, so let me draw it out more. If you were to look inside of the definition of an STL vector, you would see something similar to the following:

template <typename ValueType, typename AllocatorType>
class vector
{
public:
    typedef ValueType value_type;
...
}

The use of typedefs within the standard containers allows a generic function (like the one I created above) to reference these types. The function "Sum" is templated on the type of the container (std::vector<int>), not on the type held inside the container (int). Without typedef it would not be possible to refer to that internal type.

Therefore, typedefs are central to Modern C++ and this is not possible with Macros.

share|improve this answer
    
The question asked about macros vs typedef, not macros vs inline functions. –  Ben Voigt Jan 21 '12 at 2:05
    
Sure, and this is only possible with typedefs... that is what value_type is. –  Chris Pitman Jan 21 '12 at 5:28
    
This isn't a typedef, it is template programming. A function or functor is not a type, no matter how generic it is. –  user29079 Jan 23 '12 at 7:51
    
@Lundin I've added further details: The function Sum is only possible because standard containers use typedefs to expose the types they are templated on. I am not saying that Sum is a typedef. I am saying std::vector<T>::value_type is a typedef. Feel free to look at the source of any standard library implementation to verify. –  Chris Pitman Jan 23 '12 at 13:53
    
Fair enough. It would perhaps have been better to only post the second snippet. –  user29079 Jan 23 '12 at 15:20
add comment

"#define" will replace what you wrote after, typedef will create type. So if you want to have customer type - use typedef. If you need macros - use define.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.