Object-Oriented Programming for Scientific Computing
Smart Pointers and Constness
Ole Klein
Interdisciplinary Center for Scientific Computing Heidelberg University
ole.klein@iwr.uni-heidelberg.de
28. April 2015
C++11 and Dynamic Memory Management
Smart Pointer
C++11 offers a number of so-called smart pointers that can help manage dynamic memory and in particular ensure the correct release of allocated memory.
There are three different types of smart pointers:
• std::unique_ptr<T>
• std::shared_ptr<T>
• std::weak_ptr<T>
The template argumentTspecifies the type of object the smart pointer points to.
The C++11 smart pointers are defined in the header filememory.
• In the case ofunique_ptrs there is always exactly one smart pointer that owns the allocated data. If this pointer is destroyed (e.g. because the function in which it was defined exits or the destructor of the object to which it belongs is called), then the virtual memory is freed.
• Smart pointers and normal pointers (raw pointers) should not be mixed to avoid the risk of unauthorized access to already freed memory or double release of memory. Therefore, the allocation of memory must be placed directly in the constructor call ofunique_ptr.
• An assignment of a normal pointer to a smart pointer is not possible (but a transfer in the constructor is).
Example for
unique_ptr# include< memory >
# include< i o s t r e a m >
s t r u c t b l u b {
v o i d d o S o m e t h i n g () {}
};
int m a i n () {
std :: u n i q u e _ p t r <int> t e s t (new int) ;
t e s t = new int; // not a l l o w e d : a s s i g n m e n t f r o m raw p o i n t e r int a ;
t e s t = & a ; // not a l l o w e d : a s s i g n m e n t f r o m raw p o i n t e r std :: u n i q u e _ p t r <int> t e s t 5 (& a ) ; // a l l o w e d but d a n g e r o u s
* t e s t = 2; // n o r m a l m e m o r y a c c e s s
std :: u n i q u e _ p t r <int> t e s t 2 ( t e s t . r e l e a s e () ) ; // m o v e to o t h e r p o i n t e r
t e s t = std :: m o v e ( t e s t 2 ) ; // a s s i g n m e n t o n l y u s i n g m o v e
Example for
unique_ptrt e s t . s w a p ( t e s t 2 ) ; // e x c h a n g e w i t h o t h e r p o i n t e r
if ( t e s t == n u l l p t r ) // c o m p a r i s o n
std :: c o u t < < " t e s t is n u l l p t r " < < std :: e n d l ;
if (! t e s t 2 ) // t e s t for e x i s t e n c e
of o b j e c t
std :: c o u t < < " t e s t 2 is n u l l p t r " < < std :: e n d l ; std :: u n i q u e _ p t r <int[] > t e s t 3 (new int[ 3 2 ] ) ; // a r r a y
t e s t 3 [7] = 12; // a c c e s s to a r r a y
if ( t e s t 3 ) // a c c e s s to raw p o i n t e r
std :: c o u t < < " t e s t 3 is " < < t e s t 3 . get () < < std :: e n d l ;
t e s t 3 . r e s e t () ; // r e l e a s e of m e m o r y
if (! t e s t 3 )
std :: c o u t < < " t e s t 3 is n u l l p t r " < < std :: e n d l ;
std :: u n i q u e _ p t r < blub > t e s t 4 (new b l u b ) ; // a l l o c a t e o b j e c t test4 - > d o S o m e t h i n g () ; // use m e t h o d of o b j e c t std :: u n i q u e _ p t r < FILE , int(*) ( F I L E *) > f i l e P t r (
f o p e n (" b l u b . txt ", " w ") , f c l o s e ) ; // C r e a t e and c l o s e f i l e }
shared_ptr
• shared_ptrs point to memory that is used concurrently.
• Severalshared_ptrs can point to the same memory location. The number of simultaneousshared_ptrs to the same resource is monitored with reference counting. The allocated memory is freed when the lastshared_ptrpointing to it disappears.
• Apart from that the functionality ofshared_ptris the same as that of
unique_ptr.
• When the firstshared_ptrto an object is created, a manager object is created that manages both the allocated resources and a variable that counts how many pointers point to the resources at any given moment.
• For each copy of ashared_ptrthe counter is incremented, and it is lowered each time ashared_ptris deleted or modified to point to a different location.
If the counter reaches zero, the resources are released.
• If several objects haveshared_ptrs pointing to each other, they can be kept alive artificially after their scope ends, because each object has at least one pointer in the circle pointing to it.
• In order to break such a circuit, the classweak_ptrhas been created.
• Aweak_ptris not a real pointer. It can not be dereferenced and no methods can be invoked on it.
• Aweak_ptronly observes a dynamically allocated resource and can be used to check if it still exists.
• If access to the resource is required, the methodlock()ofweak_ptrcan be used to generate ashared_ptrto the resource. This then ensures the existence of the resource as long it is used.
• The manager object of ashared_ptrhas another counter, the so-called weak counter, which in turn counts the generatedweak_ptrs. While the allocated resource is released when noshared_ptrpoints on it, the manager object is released when in addition noweak_ptrpoints to it.
shared_ptr
to
this• Sometimes a pointer pointing atthisis needed. As one shouldn’t mix smart pointers and raw pointers, ashared_ptrtothismust be used.
• If this is realized byshared_ptr<T> blub(*this), then a new manager object will be created and the memory of the object is either not released or released to early.
• Instead, one derives the class from the template class
enable_shared_from_this<T>. A pointer tothisis then created with the methodshared_from_this:
s h a r e d _ p t r < T > b l u b = s h a r e d _ f r o m _ t h i s () ;
• During the creation of such a derived object in the constructor of a
shared_ptr, a weak_ptrto the object itself is stored within the object. The methodshared_from_thisgenerates ashared_ptrout of this storedweak_ptr.
Example for
shared_ptr# include< memory >
# include< i o s t r e a m >
c l a s s B a s e : p u b l i c std :: e n a b l e _ s h a r e d _ f r o m _ t h i s < Base >
{
v o i d d o S o m e t h i n g () {
std :: s h a r e d _ p t r < Base > m y O b j = s h a r e d _ f r o m _ t h i s () ; }
};
c l a s s D e r i v e d : p u b l i c B a s e {};
int m a i n () {
std :: s h a r e d _ p t r <int> t e s t P t r (new int) , t e s t P t r 2 ; t e s t P t r 2 = t e s t P t r ; // i n c r e a s e s s h a r e d c o u n t
std :: c o u t < < t e s t P t r . u s e _ c o u n t () < < std :: e n d l ; // n u m b e r of s h a r e d _ p t r s
t e s t P t r . r e s e t () ; // d e c r e a s e s s h a r e d count , t e s t P t r is
Example for
shared_ptr// w e a k p o i n t e r e x a m p l e
std :: w e a k _ p t r <int> w e a k P t r = t e s t P t r 2 ; // i n c r e a s e s w e a k c o u n t t e s t P t r = w e a k P t r . l o c k () ;
if ( t e s t P t r )
std :: c o u t < < " O b j e c t s t i l l e x i s t s " < < std :: e n d l ; if ( w e a k P t r . e x p i r e d () )
std :: c o u t < < " O b j e c t d o e s n ’ t e x i s t any m o r e " < < std :: e n d l ; std :: s h a r e d _ p t r <int> t e s t P t r 3 ( w e a k P t r ) ; // t h r o w s e x c e p t i o n if
o b j e c t has v a n i s h e d // C a s t i n g of s h a r e d p o i n t e r s
std :: s h a r e d _ p t r < Base > b a s e P t r (new D e r i v e d ) ; std :: s h a r e d _ p t r < Derived > d e r i v e d P t r ;
d e r i v e d P t r = std :: s t a t i c _ p o i n t e r _ c a s t < Derived >( b a s e P t r ) ; //
c r e a t e c a s t s m a r t p o i n t e r s h a r i n g o w n e r s h i p w i t h o r i g i n a l p o i n t e r
}
Constant Variables
• For constant variables the compiler ensures that the content is not changed during program execution.
• Constant variables must be initialized when they are defined.
• They can not be changed later on.
c o n s t int n u m E l e m e n t s = 1 0 0 ; // i n i t i a l i z a t i o n
n u m E l e m e n t s = 2 0 0 ; // not allowed , c o n s t
• Compared to the macros in C, constant variables are preferred, because they allow the strict type checking of the compiler.
Constant Values
Constant References
• References can be also defined as constant. The value pointed to by the reference cannot be changed (using the reference).
• Constant variables only allow constant references (since otherwise they might be changed using the reference).
int n u m N o d e s = 1 0 0 ; // v a r i a b l e
c o n s t int& nn = n u m N o d e s ; // v a r i a b l e c a n n o t be c a n g e d u s i n g nn
// but can be u s i n g n u m N o d e s c o n s t int n u m E l e m e n t s = 99; // i n i t i a l i z a t i o n
int& ne = n u m E l e m e n t s ; // not allowed , const - c o r r e c t n e s s // w o u l d n ’ t be g u a r a n t e e d a n y m o r e c o n s t int& n u m E l e m = n u m E l e m e n t s ; // a l l o w e d
• Constant references are a great way to pass a variable to a function without copying.
M a t r i x C l a s s & o p e r a t o r+=(c o n s t M a t r i x C l a s s & b ) ;
Constant Pointers
For pointers there are two different types of constness. For a pointer it may be forbidden
• to change the contents of the variable to which it points. This is expressed by writingconstbefore the type of the pointer:
c h a r s [ 1 7 ] ;
c o n s t c h a r* pc = s ; // p o i n t e r to c o n s t a n t pc [3] = ’ c ’; // error , c o n t e n t is c o n s t
++ pc ; // a l l o w e d
• to change the address stored in the pointer (such a pointer effectively acts as a reference). This is expressed by writingconstbetween the type of the pointer and the name of the pointer:
c h a r* c o n s t cp = s ; // c o n s t p o i n t e r
cp [3] = ’ c ’; // a l l o w e d
++ cp ; // error , p o i n t e r is c o n s t
Constant Values
Constant Pointers
• Of course there is also the combination of both (which corresponds to a constant reference):
c o n s t c h a r* c o n s t cpc = s ; // c o n s t p o i n t e r to c o n s t a n t
cpc [3] = ’ c ’; // error , c o n t e n t is c o n s t
++ cpc ; // error , p o i n t e r is c o n s t
Constant Objects
• Objects can also be defined as constant.
• The user assumes that the content of a constant object doesn’t change. This must be guaranteed by the implementation.
• Therefore, it isn’t allowed to call methods that could change the object.
• Functions which will not violate the constness are marked by the addition of the keywordconstafter the argument list.
• The keyword is part of the name. There can be aconstand a non-const
variant with the same argument list.
• Important: theconstmust also be added to the method definition outside of the class.
• For constant objects onlyconstmethods can be called.
Constant Values
# include< i o s t r e a m >
c l a s s X {
p u b l i c:
int b l u b () c o n s t {
r e t u r n 3;
}
int b l u b () {
r e t u r n 2;
} };
int m a i n () {
X a ;
c o n s t X & b = a ;
std :: c o u t < < a . b l u b () < < " " < < b . b l u b () < < std :: e n d l ; // p r o d u c e s the o u t p u t "2 3"
}
Of course the behavior used here for illustrative purposes is misleading and should not be used.
Example: Matrix Class
d o u b l e* M a t r i x C l a s s ::o p e r a t o r[](int i ) {
if (( i <0) ||( i >= n u m R o w s _ ) ) {
std :: c e r r < < " I l l e g a l row i n d e x " < < i ;
std :: c e r r < < " v a l i d r a n g e is (0: " < < n u m R o w s _ < < " ) ";
std :: c e r r < < std :: e n d l ; e x i t ( E X I T _ F A I L U R E ) ; }
r e t u r n a_ [ i ];
}
c o n s t d o u b l e* M a t r i x C l a s s ::o p e r a t o r[](int i ) c o n s t {
if (( i <0) ||( i >= n u m R o w s _ ) ) {
std :: c e r r < < " I l l e g a l row i n d e x " < < i ;
std :: c e r r < < " v a l i d r a n g e is (0: " < < n u m R o w s _ < < " ) ";
std :: c e r r < < std :: e n d l ; e x i t ( E X I T _ F A I L U R E ) ; }
Constant Values
Using this we may write:
M a t r i x C l a s s A (4 ,6 ,0.0) ; for (int i =0; i < A . R o w s () ;++ i )
A [ i ][ i ] = 2 . 0 ;
c o n s t M a t r i x C l a s s E (5 ,5 ,1.0) ; for (int i =0; i < E . R o w s () ;++ i )
std :: c o u t < < E [ i ][ i ] < < std :: e n d l ;
Returning a pointer to a constant will prevent the object being implicitly modified by the return value:
A [ 2 ] [ 3 ] = -1.0; // ok , no c o n s t a n t E [ 1 ] [ 1 ] = 0 . 0 ; // c o m p i l e r e r r o r
Physical and Logical Constness
When is a methodconst?
1 The object remains bitwise unchanged. That’s how the compiler sees it (that’s all it can check) and what it tries to ensure e.g. by treating all data members of aconstobject also as constants. This is also known as physical constness.
2 The object remains conceptually unchanged for the user of the class. This is referred to as a logical constness. But the compiler is unable to check the semantics.
Constant Values
Physical Constness and Pointers
• In our matrix class example with dynamic memory management, we have used a pointer of typedouble **to store the matrix.
• Making this pointer constant we obtain a pointer of typedouble ** const. This way it’s only forbidden to change the memory address which is stored in the pointer but not the entries in the matrix.
• The compiler doesn’t complain about the definition:
d o u b l e& M a t r i x C l a s s ::o p e r a t o r() (int i , int j ) c o n s t;
This therefore allows changing a constant object:
c o n s t M a t r i x C l a s s E (5 ,5 ,1.0) ; E (1 ,1) = 0 . 0 ;
• It is even allowed to change the entries within the class itself:
d o u b l e& M a t r i x C l a s s ::o p e r a t o r() (int i ,int j ) c o n s t {
a_ [ 0 ] [ 0 ] = 1 . 0 ; r e t u r n a_ [ i ][ j ];
}
Alternatives
• Using an STL container as in the first variant of the matrix class:
std :: vector < std :: vector <double> >
• In aconstobject this becomes aconst std::vector<std::vector<double> >.
• Defining the access function
d o u b l e& M a t r i x C l a s s ::o p e r a t o r() (int i , int j ) c o n s t;
results in an error message from the compiler:
m a t r i x . cc : In m e m b e r f u n c t i o n ’ d o u b l e &
M a t r i x C l a s s :: o p e r a t o r () ( int , int ) c o n s t ’:
m a t r i x . cc : 6 3 : e r r o r : i n v a l i d i n i t i a l i z a t i o n of r e f e r e n c e of t y p e ’ d o u b l e & ’ f r o m e x p r e s s i o n of t y p e ’ c o n s t d o u b l e ’
Constant Values
Alternatives (II)
• Returning entire vectors with:
std :: vector <double>& M a t r i x C l a s s ::o p e r a t o r[](int i ) c o n s t;
fails as well:
m a t r i x . cc : In m e m b e r f u n c t i o n ’ std :: vector < double ,
std :: a l l o c a t o r < double > >& M a t r i x C l a s s :: o p e r a t o r []( int ) c o n s t ’:
m a t r i x . cc : 8 7 : e r r o r : i n v a l i d i n i t i a l i z a t i o n of r e f e r e n c e of t y p e ’ std :: vector < double , std :: a l l o c a t o r < double > >& ’ f r o m e x p r e s s i o n of t y p e ’ c o n s t std :: vector < double ,
std :: a l l o c a t o r < double > > ’
Note: Using pointers it is easy to circumvent the compiler functionality for monitoring physical constness. Therefore it is appropriate to exercise caution when usingconstmethods for objects that use dynamically allocated memory.
Logical Constness and Caches
• Sometimes it is useful to store calculated values with high computational cost in order to save computing time when they are needed several times.
• We add the private variablesdouble norm_andbool normIsValid_to the matrix class and make sure thatnormIsValid_will always be initialized withfalsein the constructor.
Constant Values
Logical Constness and Caches
• Then it is possible to implement an infinity norm as follows:
d o u b l e M a t r i x C l a s s :: I n f i n i t y N o r m () {
if (! n o r m I s V a l i d _ ) {
n o r m _ = 0.;
for (int j = 0; j < n u m C o l s _ ; ++ j ) {
d o u b l e sum = 0.;
for (int i = 0; i < n u m R o w s _ ; ++ i ) sum += f a b s ( a_ [ i ][ j ]) ;
if ( sum > n o r m _ ) n o r m _ = sum ; }
n o r m I s V a l i d _ = t r u e; }
r e t u r n n o r m _ ; }
• This function also makes sense for a constant matrix and doesn’t semantically violate the constness.
Solution
• One defines both variables asmutable.
m u t a b l e b o o l n o r m I s V a l i d _ ; m u t a b l e d o u b l e n o r m _ ;
• Members that aremutable can also be modified inconstobjects.
• This should only be applied when it’s absolutely necessary and doesn’t change the logical constness of the object.
Constant Values
Friend
In some cases it may be necessary for other classes or functions to access the protected members of a class.
Example:Simply linked list
• Nodecontains the data.
• Listshould be able to change the data ofNode.
• The data ofNodeshould be private.
• Listisfriend ofNodeand may therefore access private data.
Friend II
• Classes and free functions can befriendof another class.
• Such afriendmay access the private data of the class.
Examplefriendclass:
c l a s s L i s t ; c l a s s N o d e {
p r i v a t e:
N o d e * n e x t ; p u b l i c:
int v a l u e ;
f r i e n d c l a s s L i s t ; };
Constant Values
Friend II
• Classes and free functions can befriendof another class.
• Such afriendmay access the private data of the class.
Examplefriendfunction:
c l a s s M a t r i x C l a s s {
f r i e n d M a t r i x C l a s s i n v e r t (c o n s t M a t r i x C l a s s &) ; // ...
};
...
M a t r i x C l a s s A ( 1 0 ) ; ...
M a t r i x C l a s s inv = i n v e r t ( A ) ;
Friend III
• Almost everything that can be written as a class method can also be programmed as a freefriendfunction.
• All classes and functions which arefriendlogically belong to the class, as they build on its internal structure.
• Avoidfriend declarations. They open up encapsulation and raise the cost of maintenance.
Build Systems
Build Systems
• Complex projects consist of various programs and libraries.
• Each program / library consists of many files (header and source files).
• Build systems are created to make things easier when compiling.
Goal:
• A build system knows how to create the programs and libraries from the files.
• If a file is modified, the project should be updated.
• Typically not all files must be recompiled.
• Recompile as many files as necessary . . . and as few as possible.
Build Systems
• Complex projects consist of various programs and libraries.
• Each program / library consists of many files (header and source files).
• Build systems are created to make things easier when compiling.
Goal:
• A build system knows how to create the programs and libraries from the files.
• If a file is modified, the project should be updated.
• Typically not all files must be recompiled.
• Recompile as many files as necessary . . . and as few as possible.
Build Systems
Choice of Build Systems
There are several different systems with different ranges of functionality:
• make
• mk
• SCons
• ant
• jam
• Rant
• built-ins or plugins of the IDE
• . . .
Moreover, there are
meta-systems which generate input files for other systems:
• automake/autoconf
• cmake
• qmake
• mkmf
• . . .
Choice of Build Systems
There are several different systems with different ranges of functionality:
• make
• mk
• SCons
• ant
• jam
• Rant
• built-ins or plugins of the IDE
• . . .
Moreover, there are
meta-systems which generate input files for other systems:
• automake/autoconf
• cmake
• qmake
• mkmf
• . . .
Build Systems
Makefiles
• makeis a program that allows to translate only the files that have changed since the last compilation.
• AMakefiledescribes the files which belong to a project and how they are compiled and linked.
• Makefilerules are written in a functional language.
• One describestargets, which depend onprerequisites.
• targetsandprerequisitesnormally correspond to files.
• For individualtargetsone creates rules that define how they are generated using thererequisites.
• Makefilerules are of the form
target - n a m e : p r e r e q u i s i t e s - l i s t build - r u l e
with the actual rule being indented with aTAB.
Easy Makefile Example
all : t e s t _ r a t i o n a l f a r e y f a r e y : f a r e y . o r a t i o n a l . o
g ++ f a r e y . o r a t i o n a l . o - o f a r e y t e s t _ r a t i o n a l : r a t i o n a l _ t e s t . o r a t i o n a l . o
g ++ r a t i o n a l _ t e s t . o r a t i o n a l . o - o t e s t _ r a t i o n a l r a t i o n a l _ t e s t . o : r a t i o n a l _ t e s t . cc r a t i o n a l . h
g ++ - c r a t i o n a l _ t e s t . cc r a t i o n a l . o : r a t i o n a l . cc r a t i o n a l . h
g ++ - c r a t i o n a l . cc f a r e y . o : f a r e y . cc r a t i o n a l . h
g ++ - c r a t i o n a l . cc
Build Systems
Advanced Makefile Example
# the c o m p i l e r we w a n t to use CXX = g ++
# s o m e m o r e v a r i a b l e s C P P S R C =$( w i l d c a r d *. cc ) O B J S =$( C P P S R C :. cc =. o ) A P P S = t e s t _ r a t i o n a l f a r e y
### b u i l d all a p p s all : $( A P P S )
# how to b u i l d the a p p s f a r e y : f a r e y . o r a t i o n a l . o
t e s t _ r a t i o n a l : r a t i o n a l _ t e s t . o r a t i o n a l . o
# how to c o m p i l e a p p s
%: %. o
$( CXX ) $? - o $@
# how to c o m p i l e o b j e c t f i l e s
%. o : %. cc
$( CXX ) $( C X X F L A G S ) - c - o $@ $<
• makesupports variables.
• Rules can be formulated generically for differenttargets,
• . . . using several automatic variables.
• GNU makehas several special extensions (e.g. wildcards).
• GNU makehas several rules already built in.
• makecan also directly clean up the created files
• and automatically check the dependencies with the help of the compiler.
Advanced Makefile Example
# the c o m p i l e r we w a n t to use CXX = g ++
CC =$( CXX )
# s o m e m o r e v a r i a b l e s C P P S R C =$( w i l d c a r d *. cc ) O B J S =$( C P P S R C :. cc =. o ) A P P S = t e s t _ r a t i o n a l f a r e y
### b u i l d all a p p s all : $( A P P S )
# how to b u i l d the a p p s f a r e y : f a r e y . o r a t i o n a l . o
t e s t _ r a t i o n a l : r a t i o n a l _ t e s t . o r a t i o n a l . o
# we use i m p l i c i t c o m p i l a t i o n r u l e s
• makesupports variables.
• Rules can be formulated generically for differenttargets,
• . . . using several automatic variables.
• GNU makehas several special extensions (e.g. wildcards).
• GNU makehas several rules already built in.
• makecan also directly clean up the created files
• and automatically check the dependencies with the help of the compiler.
Build Systems
Advanced Makefile Example
# the c o m p i l e r we w a n t to use CXX = g ++
CC =$( CXX )
# s o m e m o r e v a r i a b l e s C P P S R C =$( w i l d c a r d *. cc ) O B J S =$( C P P S R C :. cc =. o ) A P P S = t e s t _ r a t i o n a l f a r e y
### b u i l d all a p p s all : $( A P P S )
# how to b u i l d the a p p s f a r e y : f a r e y . o r a t i o n a l . o
t e s t _ r a t i o n a l : r a t i o n a l _ t e s t . o r a t i o n a l . o
# we use i m p l i c i t c o m p i l a t i o n r u l e s
### D e p e n d e n c i e s dep : . d e p e n d s
# i n c l u d e . d e p e n d s if f i l e e x i s t s - i n c l u d e . d e p e n d s
# how to c r e a t e . d e p e n d s . d e p e n d s : $( SRC )
$( CXX ) - MM $? > . d e p e n d s
### c l e a n u p c l e a n :
$( A P P S ) $( O B J S )
• makesupports variables.
• Rules can be formulated generically for differenttargets,
• . . . using several automatic variables.
• GNU makehas several special extensions (e.g. wildcards).
• GNU makehas several rules already built in.
• makecan also directly clean up the created files
• and automatically check the dependencies with the help of the compiler.
Alternative: IDEs
• Integrated Development Environments (IDEs) combine the properties of an editor, a build system and a debugger
• e.g. Eclipse C/C++ Development Environment (http://www.eclipse.org/cdt)
• Eclipse is powerful and open source, but also very complex
Build Systems
Multiple Header Inclusion Prevention
• One can use macros to prevent the repeated inclusion of a header file.
• The content of the header file is put inside a conditional block:
# i f n d e f _ M Y S P E C I A L H E A D E R F I L E _
# d e f i n e _ M Y S P E C I A L H E A D E R F I L E _ // c o n t e n t of h e a d e r f i l e
# e n d i f
• The first inclusion of the header results in the definition of the macro and parsing of the content of the header file.
• Subsequent inclusions skip the content, since the macro is already defined.