Calling constructors with placement new - Embedded.com

Calling constructors with placement new

ESC Boston 2011 speaker logo In C++, classes are usually the best tool for modeling memory-mapped devices.1 You can use a class to hide messy details, such as device register layouts and bit masks, behind a simpler and more robust interface.

A constructor is a special class member function that provides guaranteed initialization for objects of its class type.2 Using constructors to initialize objects is common practice in C++. C++ programmers should reasonably expect classes for memory-mapped devices to do initialization via constructors, unless there's some compelling reason to do otherwise.

In many embedded systems, the appropriate way to initialize a device is to put it into an inactive state. For example, the constructor for a programmable timer might simply make sure that the timer is disabled, as in:

class timer_type    {public:    ~~~    timer_type() { disable(); }    ~~~    };   

Last month, I explained the problem with getting constructors for memory-mapped devices to execute automatically.3 This month, I'll explore one way to overcome it.

Recapping the problem
With most C and C++ compilers, you can name a memory-mapped object using a standard extern declaration such as:

extern timer_type the_timer;   

and then use the linker to map the_timer to the desired address. However, this declaration is not a definition, so the compiler doesn't generate code to allocate storage or call a constructor.

Some C and C++ compilers provide a nonstandard language extension that lets you position an object at a specified memory address, such as:

timer_type the_timer @ 0xFFFF6000;   

With most compilers that support this sort of declaration, this isn't a definition, so the compiler won't generate a constructor call for this, either.

Defining a pointer or reference to a memory-mapped object, such as:

timer_type &the_timer    = *reinterpret_cast(0xFFFF6000);   

has the same initialization problem as the previous object declarations. This reference definition doesn't define the memory-mapped object itself. Here, too, the compiler won't generate a constructor call applied to the memory-mapped object.

If the compiler won't call a constructor implicitly, why not just write an explicit call, say, immediately after the object or reference declaration? That is, if you declare the timer object as:

extern timer_type the_timer;   

why not write an explicit constructor call to go with it, such as:

the_timer.timer_type();     // ?   

Because it won't compile. C++ won't let you call a constructor as if it were any other class member function. In taking on the job of generating constructor calls automatically, C++ denies you the ability to do it yourself. Well, almost.

In truth, although you can't call a constructor using the usual member function call syntax, you can call a constructor using a particular form of new-expression known as placement new-expression . To appreciate how it works, let's first review new-expressions in general.

New-expressions
In C++, you typically allocate dynamic storage using a new-expression such as:

pt = new T;    

where T is a type and pt is an object of type “pointer to T “. When T is a class type, the new-expression not only allocates storage, but also invokes a constructor. Thus, using new is generally preferable to using the standard malloc function. Whereas malloc allocates raw storage of indeterminate value, new can create objects with coherent initial values.

A new-expression allocates memory by calling a function named operator new . Each C++ environment provides a default global allocation function declared as:

void *operator new(std::size_t n);   

As with malloc , the argument to operator new is the size (in bytes) of the storage request, and the return value is the address of the allocated storage. However, operator new reports failure differently from malloc . Whereas malloc returns a null pointer if it can't allocate the requested storage, operator new throws an exception.4

Thus, for a class type T , a new-expression such as:

pt = new T;    

translates more-or-less into something like:

pt = static_cast(operator new(sizeof(T)));pt->T();    

The first statement acquires storage for a T object by calling operator new , and converts the address of that storage from type void * to type T * . The second initializes the storage by applying T 's default constructor. As I mentioned earlier, a C++ compiler won't let you write this explicit constructor call, but it's happy to do it for you.

If class T has a constructor that accepts arguments, you can get the new-expression to call that constructor by providing the constructor arguments as part of the new-expression, as in:

pt = new T (x, y, z);.   

In this case, the new-expression translates more-or-less into something like:

pt = static_cast(operator new(sizeof(T)));pt->T(x, y, z);    

Overloading operator new
As with any other function, you can overload operator new by simply declaring additional functions with the same name but different parameter types. For example, in addition to the standard allocation function:

void *operator new(std::size_t n);   

you might declare:

void *operator new(std::size_t n, other_info i );   

where other_info is some user-defined type for conveying additional information to the allocation function. That's simple enough, but how can you get a new-expression to use this alternative allocation function?

The problem is finding some way to pass the additional argument to operator new . You can't add a parenthesized argument list after the type name in the new-expression because the compiler will interpret that list as arguments to a constructor, not as arguments to an operator new . That is:

pt = new T (x);    

passes x as an argument to a T constructor, not as an additional argument to operator new .

Rather, you must squeeze the additional argument(s) to the allocation function into some other spot in the new-expression. That spot is between the keyword new and the allocated type. For example, the new-expression in:

other_info info ;~~~pt = new (info ) T;   

specifies info as an additional argument to operator new . As always, the new-expression uses sizeof(T) as the first argument to operator new . Thus, the new-expression results in a call to:

operator new(sizeof(T) , info ).

Compilers apply the usual rules for argument matching in overload resolution to find an allocation function that will accept this assembled argument list.5 The new-expression won't compile if no operator new is visible that will accept the given arguments.


Placement new
Bjarne Stroustrup, the designer of C++, explained that his primary motivation for extending the syntax of new-expressions was to pass information about where to place the created object.6 Hence, he dubbed the argument list after the keyword new the placement syntax . A new-expression that includes the placement syntax is called new with placement or just placement new .

The Standard C++ Library provides a placement form of operator new declared in the standard header as:

void *operator new(std::size_t, void *p) throw ();   

Most C++ implementations define it as an inline function:

inlinevoid *operator new(std::size_t, void *p) throw ()    {    return p;    }   

It does nothing but return the value of its second parameter. It completely ignores its first parameter. The exception-specification throw () indicates that the function isn't allowed to propagate any exceptions.

Programs can use this operator new to construct an object at a particular address. For example, if you declare the timer object as:

extern timer_type the_timer;   

you can apply the constructor via the placement new-expression:

new (&the_timer) timer_type;   

This placement new-expression compiles into a call to placement operator new , followed immediately by a call to the constructor:

operator new(sizeof(timer_type), &the_timer);(&the_timer)->timer_type();   

Recall that this operator new does nothing but return its second argument, and it's typically defined as an inline function. Most compilers completely eliminate the first statement during optimization.

The second statement:

(&the_timer)->timer_type();   

is equivalent to:

the_timer.timer_type();   

which applies the timer_type constructor to the_timer . But, again, these notations are just what I'm using to convey the concept of a constructor call. Neither one actually compiles if you try to write it yourself.

If you define the timer as a constant pointer:

timer_type *const the_timer    = reinterpret_cast(0xFFFF6000);   

then you can apply the constructor via the placement new-expression:

new (the_timer) timer_type;   

This placement new-expression differs from the earlier ones only in that it omits the unary & (address-of) operator from the placement argument: the_timer instead of &the_timer .

The C++ Standard doesn't specify the order of implicit initialization as precisely as some programmers would like. I've heard many embedded developers express a desire for more explicit control over initialization order. Using placement new offers that control.

For example, if you have some master device that must be initialized before some slave device, just write the calls to placement new in that order within some larger initialization function, as in:

void initialize()    {    new (&the_master) master_type;    new (&the_slave) slave_type;    }   

Still room for improvement
C++ programmers expect classes to use constructors to perform object initialization. Classes for memory-mapped devices should be no different. However, many common declarations for such objects don't invoke constructors implicitly. Placement new offers a way to invoke constructors explicitly, which might be what you want sometimes, but not necessarily always. At other times, implicit initialization might still be preferable. I'll be looking at other more flexible alternatives in the future.

Dan Saks is president of Saks & Associates, a C/C++ training and consulting company. For more information about Dan Saks, visit his website at www.dansaks.com. Dan also welcomes your feedback: e-mail him at .

Endnotes:
1. Saks, Dan. “Memory-mapped devices as C++ classes,” Embedded.com, June 2010. www.eetimes.com/4200572 .
2. Saks, Dan. “Demystifying constructors,” Embedded Systems Design , January/February 2011, p. 9. www.eetimes.com/4212701 .
3. Saks, Dan. “Difficulties constructing memory-mapped objects,” Embedded.com, August 2011. www.eetimes.com/4218361 .
4. Saks, Dan. “Throw and catch,” ESD, May, 2007, p. 11. www.eetimes.com/4026038 .
5. Saks, Dan. “Function Name Overloading,” Embedded Systems Programming , May 1999, p. 17. www.eetimes.com/4026899 .
6. Bjarne Stroustrup. The Design and Evolution of C++ . Addison-Wesley, 1994.

This article provided courtesy of Embedded.com and Embedded Systems Design magazine.
See more articles like this one on Embedded.com.
See more columns from Dan Saks.
This material was first printed in Embedded Systems Design magazine.
Sign up for subscriptions and newsletters.
Copyright © 2011
UBM–All rights reserved.

2 thoughts on “Calling constructors with placement new

  1. “This only applies to replacing operator new. But nothing is preventing a user from overloading that operator new in another namespace. You'd still have to make sure that the appropriate “do nothing” operator is called by rewriting your code as “::new (

    Log in to Reply
  2. “I have used this placement new in the past. It was quite useful for an accessor class to many memory mapped peripheral, such as UART. The memory map of the peripheral was identical except for the base address of the register area. So one class which al

    Log in to Reply

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.