C++ feature descriptions
When you write C++ source code for the IAR C/C++ Compiler for RL78, you must be aware of some benefits and some possible quirks when mixing C++ features—such as classes, and class members—with IAR language extensions, such as IAR-specific attributes.
Static data members of C++ classes are treated the same way global variables are, and can have any applicable IAR type, memory, and object attribute.
Member functions are in general treated the same way free functions are, and can have any applicable IAR type, memory, and object attributes. Virtual member functions can only have attributes that are compatible with default function pointers, and constructors and destructors cannot have any such attributes.
The location operator @ and the #pragmalocation directive can be used on static data members and with all member functions.
Example of using attributes with classes
class MyClass
{
public:
// Locate a static variable in __saddr memory at
// address 0xFFE30
static __saddr __no_init int mI @ 0xFFE30;
// Locate a static function in __near_func memory
static __near_func void F();
// Locate a function in __far_func memory
__far_func void G();
// Locate a virtual function in __near_func memory
virtual __near_func void H();
// Locate a virtual function into SPECIAL
virtual void M() const volatile @ "SPECIAL";
};The this pointer
The this pointer used for referring to a class object or calling a member function of a class object will by default have the data memory attribute for the default data pointer type. This means that such a class object can only be defined to reside in memory from which pointers can be implicitly converted to a default data pointer. This restriction might also apply to objects residing on a stack, for example temporary objects and auto objects.
Class memory
To compensate for this limitation, a class can be associated with a class memory type. The class memory type changes:
the
thispointer type in all member functions, constructors, and destructors into a pointer to class memorythe default memory for static storage duration variables—that is, not auto variables—of the class type, into the specified class memory
the pointer type used for pointing to objects of the class type, into a pointer to class memory.
Example
class __far C { public: void MyF(); // Has a this pointer of type C __far * void MyF() const; // Has a this pointer of type // C __far const * C(); // Has a this pointer pointing into far // memory C(C const &); // Takes a parameter of type C __far // const & (also true of generated copy // constructor) int mI; }; C Ca; // Resides in far memory instead of the // default memory C __near Cb; // Resides in near memory, the 'this' // pointer still points into far memory void MyH() { C cd; // Resides on the stack } C *Cp1; // Creates a pointer to far memory C __near *Cp2; // Creates a pointer to near memory
Whenever a class type associated with a class memory type, like C, must be declared, the class memory type must be mentioned as well:
class __far C;Also note that class types associated with different class memories are not compatible types.
A built-in operator returns the class memory type associated with a class, __memory_of(class). For instance, __memory_of(C) returns __far.
When inheriting, the rule is that it must be possible to convert implicitly a pointer to a subclass into a pointer to its base class. This means that a subclass can have a more restrictive class memory than its base class, but not a less restrictive class memory.
class __far D : public C { // OK, same class memory public: void MyG(); int mJ; }; class __near E : public C { // OK, near memory is inside far public: void MyG() // Has a this pointer pointing into near memory { MyF(); // Gets a this pointer into far memory } int mJ; }; class F : public C { // OK, will be associated with same class memory as C public: void MyG(); int mJ; };
A new expression on the class will allocate memory in the heap associated with the class memory. A delete expression will naturally deallocate the memory back to the same heap. To override the default new and delete operator for a class, declare
void *operator new(size_t); void operator delete(void *);
as member functions, just like in ordinary C++.
For more information about memory types, see Memory types.
C++ supports templates according to the C++ standard. The implementation uses a two-phase lookup which means that the keyword typename must be inserted wherever needed. Furthermore, at each use of a template, the definitions of all possible templates must be visible. This means that the definitions of all templates must be in include files or in the actual source file.
Templates and data memory attributes
For data memory attributes to work as expected in templates, two elements of the standard C++ template handling were changed—class template partial specialization matching and function template parameter deduction.
In C++, the class template partial specialization matching algorithm works like this:
When a pointer or reference type is matched against a pointer or reference to a template parameter type, the template parameter type will be the type pointed to, stripped of any data memory attributes, if the resulting pointer or reference type is the same.
Example
// We assume that __far is the memory type of the default // pointer. template<typename> class Z {}; template<typename T> class Z<T *> {}; Z<int __near *> Zn; // T = int __near Z<int __far *> Zf; // T = int Z<int *> Zd; // T = int
In C++, the function template parameter deduction algorithm works like this:
When function template matching is performed and an argument is used for the deduction—if that argument is a pointer to a memory that can be implicitly converted to a default pointer—do the parameter deduction as if it was a default pointer.
When an argument is matched against a reference, do the deduction as if the argument and the parameter were both pointers.
Example
// We assume that __far is the memory type of the default // pointer. template<typename T> void fun(T *); void MyF() { fun((int __near *) 0); // T = int. The result is different // than the analogous situation with // class template specializations. fun((int *) 0); // T = int fun((int __far *) 0); // T = int }
For templates that are matched using this modified algorithm, it is impossible to get automatic generation of special code for pointers to “small” memory types. For “large” and “other” memory types (memory that cannot be pointed to by a default pointer) it is possible. To make it possible to write templates that are fully memory-aware—in the rare cases where this is useful—use the #pragma basic_template_matching directive in front of the template function declaration. That template function will then match without the modifications described above.
Example
// We assume that __far is the memory type of the default // pointer. #pragma basic_template_matching template<typename T> void fun(T *); void MyF() { fun((int __near *) 0); // T = int __near }
The C++11 keywords auto and decltype behave exactly like function template parameter deduction. That is, if deduction is made from a pointer type that can be implicitly converted to a default pointer type, the default pointer type is used instead.
Example
__memattr1 int var1; __memattr2 int var2; __memattr3 int var3; auto p1 = &var1; // type is "int __memattr2 *" auto p2 = &var2; // type is "int __memattr2 *" auto p3 = &var3; // type is "int __memattr3 *" decltype(&var1) q1; // type is "int __memattr2 *" decltype(&var2) q2; // type is "int __memattr2 *" decltype(&var3) q3; // type is "int __memattr3 *"
A function type with extern"C" linkage is compatible with a function that has C++ linkage.
Example
extern "C"
{
typedef void (*FpC)(void); // A C function typedef
}
typedef void (*FpCpp)(void); // A C++ function typedef
FpC F1;
FpCpp F2;
void MyF(FpC);
void MyG()
{
MyF(F1); // Always works
MyF(F2); // FpCpp is compatible with FpC
}There are operators for new and delete for each memory that can have a heap, that is, saddr, near, far, and hugememory.
#include <stddef.h> // Assumes that there are heaps for __near, __far, and __huge void __far *operator new __far (__far_size_t); void __near *operator new __near(__near_size_t); void __huge *operator new __huge (__huge_size_t); void operator delete(void __far *); void operator delete(void __near *); void operator delete(void __huge *); // And correspondingly for array new and delete operators void __far *operator new[] __far (__far_size_t); void __near *operator new[] __near(__near_size_t); void __huge *operator new[] __huge(__huge_size_t); void operator delete[](void __far *); void operator delete[](void __near *); void operator delete[](void __huge *);
Use this syntax if you want to override both global and class-specific operator new and operator delete for any data memory.
Note that there is a special syntax to name the operator new functions for each memory, while the naming for the operator delete functions relies on normal overloading.
New and delete expressions
A new expression calls the operator new function for the memory of the type given. If a class, struct, or union type with a class memory is used, the class memory will determine the operator new function called. For example,
void MyF()
{
// Calls operator new __near(__near_size_t)
int __near *p = new __near int;
// Calls operator new __near(__near_size_t)
int __near *q = new int __near;
// Calls operator new[] __near(__near_size_t)
int __near *r = new __near int[10];
// Calls operator new __far(__far_size_t)
class __far S
{
};
S *s = new S;
// Calls operator delete(void __near *)
delete p;
// Calls operator delete(void __far *)
delete s;
int __far *t = new __near int;
delete t; // Error: Causes a corrupt heap
}Note that the pointer used in a delete expression must have the correct type, that is, the same type as that returned by the new expression. If you use a pointer to the wrong memory, the result might be a corrupt heap.
If interrupt functions use static class objects that need to be constructed (using constructors) or destroyed (using destructors), your application will not work properly if the interrupt occurs before the objects are constructed, or, during or after the objects are destroyed.
To avoid this, make sure that these interrupts are not enabled until the static objects have been constructed, and are disabled when returning from main or calling exit. For information about system startup, see System startup and termination.
Function local static class objects are constructed the first time execution passes through their declaration, and are destroyed when returning from main or when calling exit.
To handle memory exhaustion, you can use the set_new_handler function.
If you do not call set_new_handler, or call it with a NULL new handler, and operatornew fails to allocate enough memory, it will call abort. The nothrow variant of the new operator will instead return NULL.
If you call set_new_handler with a non-NULL new handler, the provided new handler will be called by operatornew if operatornew fails to allocate memory. The new handler must then make more memory available and return, or abort execution in some manner. The nothrow variant of operatornew will never return NULL in the presence of a new handler.
This is the same behavior as using the nothrow variants of new.
A pointer to a member function can only contain a default function pointer, or a function pointer that can implicitly be cast to a default function pointer. To use a pointer to a member function, make sure that all functions that should be pointed to reside in the default memory or a memory contained in the default memory.
Example
class X
{
public:
__near_func void F();
};
void (__near_func X::*PMF)(void) = &X::F;The C-SPY debugger has built-in display support for the STL containers. The logical structure of containers is presented in the watch views in a comprehensive way that is easy to understand and follow.
For more information, see General C-SPY debugger features.