9.10 Compiler-Specific Type Qualifiers - C++

The C++ language does not support additional type qualifiers such as named address spaces and _Fract or _Accum . These features are simply not available when compiled with a C++ compiler, even if they are wrapped in an extern "C" block.

For MPLAB® XC-DSC C++ Compiler for dsPIC33C/E/F and dsPIC30 DSC these type qualifiers are the extended address space qualifiers such as __prog__ and __eds__. The space attribute which defines the allocation requirements is available in C++.

Our recommend approach is to use function accessors written in C and call these from C++; these accessor functions should be prototyped in C++ within an extern "C" block.

In the following brief example, we intend to place a data table in the PSV section and share this table with C++ code in other modules. There are 2 main aspects on the 'C' side, the declaration (prototypes) and the implementation. We share an example C++ code that safely uses this data.

Declarations - interface.h

#ifndef _INTERFACE_H_
#define _INTERFACE_H_
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

/* access function */
extern const char *PSV_read_char_ptr_from(uint32_t handle, int index);

/* an opaque type of suitable size; usually 'void *' would be used */
extern uint32_t handle_1;
extern uint32_t handle_2;

#ifdef __cplusplus
}
#endif

#endif

Implementation - implementation.c

#include <stdio.h>
#include "interface.h"

/* a __psv__ pointer to a const char (in data space) */
static const char * __psv__ data1[] __attribute__((space(psv))) = {
  "Hello World",
  "Is there anybody out there?",
  "En Fin!"
};

static const char * __psv__ data2[]  __attribute__((space(psv))) = {
  "Bonjour le monde",
  "Il y a quelqu'un?",
  "The end!"
};

uint32_t handle_1 = 0;
uint32_t handle_2 = 0;

void __attribute__((user_init)) implementation_init(void) {
  /* cannot initialise this statically
   * - user_init attribute is a way to do C++ static initialisation from C
   */
  handle_1 = (uint32_t)data1;
  handle_2 = (uint32_t)data2;
}

const char * PSV_read_char_ptr_from(uint32_t handle, int index) {
  char  * __psv__ *p = (char * __psv__ *)handle;

  if (index < 0) return 0;
  return p[index];
}

C++ use - main.cpp

#include <stdio.h>
#include "interface.h"

int main(void) {
  for (int index = 0; index < 3; index++) {
    char *p = PSV_read_char_ptr_from(handle_1,index);
    fprintf(stderr,"message %d: %p %s\n", index, p, p);
  }

  for (int index = 0; index < 3; index++) {
    char *p = PSV_read_char_ptr_from(handle_2,index);
    fprintf(stderr,"message %d: %p %s\n", index, p, p);
  }
  return 0;
}

Compilation steps

Compilation is straight forward; compile the C program with the C compiler, compile the C++ program with the C++ compiler and link the resulting objects together. There is one caveat: for historical reasons the C compiler defaults to -fshort-double but the C++ compiler does not support that. Be sure use the -fno-short-double option with C compiled source when intermixing.

  1. xc-dsc-gcc -c implementation.c -O1 -mcpu=30F6014 -fno-short-double
  2. xc-dsc-g++ implementation.o main.cpp -O1-mcpu=30F6014

Executing this in the simulator should display the following, addresses may change:

message 0: 9050 Hello World
message 1: 905c Is there anybody out there?
message 2: 9078 En Fin!
message 0: 9024 Bonjour le monde
message 1: 9035 Il y a quelqu'un?
message 2: 9047 The end!