3

I'm working on a C++ written code and trying to convert it to be C to build on an embedded platform.

I wondering how to convert templates effectively to do the same function in C without changing the rest of the code.

here is an example file from my project:

template <typename T,int width,int depth>
void relu(T arr[][width][depth],T layermap[][width][depth]){
     for(int k=0;k<depth;k++)
     {
       for(int j=0;j<width;j++)
       {
           for (int i=0;i<width;i++)
           {
              /*max*/
             layermap[i][j][k]=((arr[i][j][k]>0)? arr[i][j][k]:0);
           }
       }
     }

}
  • 4
    Specialize all templates manually (and prepare also for *lots* of other work) – Michael Chourdakis Feb 10 '20 at 14:54
  • I'm sorry it was a trial to solve the problem, the original code in mentioned below:\ – Kareem Abdelmoniem Feb 10 '20 at 15:01
  • 1
    This comment is somewhat off topic, but have you thought about using C++ for your embedded application? – Johan Feb 10 '20 at 15:01
  • 6
    An off-topic, but I highly recommend to reverse the for loops to go i,j,k instead of k,j,i. Then it will access the contiguous memory region with hot CPU cache lines. The performance boost will be noticeable. – PooSH Feb 10 '20 at 15:02
  • Simply switch the template type for the type(s) that actually makes sense in the application. – Lundin Feb 10 '20 at 15:03
  • It even seems that c++ version should use `const T (&arr)[width][width][depth]` – Jarod42 Feb 10 '20 at 15:04
  • Specialise the templates (manually) and add the specialised type(s) to the name of the function (as C does not have function overloading). eg `void relu_float(float arr[][width][depth], float layermap[][width][depth]) { ... }` – Richard Critten Feb 10 '20 at 15:04
  • 1
    sure, but the sdk I'm working on doesn't support building c++ files so I'm trying to convert it to C files since there is no specific C++ functions used (only templates) – Kareem Abdelmoniem Feb 10 '20 at 15:04
  • sometimes I hear templates being compared with macros, Imho this is completely inappropriate, but in this case you either do it all manually or use macros... – 463035818_is_not_an_ai Feb 10 '20 at 15:05
  • 2
    Prepare to duplicate a *lot* of code. Seriously, why do this? Why not just use the C++ code as-is and add wrappers to call it from C? – Jesper Juhl Feb 10 '20 at 15:05
  • @RichardCritten how can I do something like that? – Kareem Abdelmoniem Feb 10 '20 at 15:06
  • 1
    @KareemAbdelmoniem lots of typing – Richard Critten Feb 10 '20 at 15:07
  • Issue is that multidimensional array (and not pointer of pointer of pointer) is not fine to pass as argument in C. Changing `T arr[width][width][depth]` to `T arr[width*width*depth]` (and so `T*arr, int width, int depth`) would be easier. – Jarod42 Feb 10 '20 at 15:07
  • 2
    @KareemAbdelmoniem "@RichardCritten how can I do something like that?" - with lots of hard manual work.. C++ templates were invented for a *reason*. To *avoid* lots of manual work. No templates? Welcome back manual work. – Jesper Juhl Feb 10 '20 at 15:08
  • 2
    @Jarod42 *Issue is that multidimensional array (and not pointer of pointer of pointer) is not fine to pass as argument in C.* No, it's C++ that doesn't support VLAs. See https://stackoverflow.com/questions/48901482/passing-array-to-a-function-and-why-it-does-not-work-in-c – Andrew Henle Feb 10 '20 at 15:12
  • You need to realize that C and C++ are *entirely different* languages and converting between them is *not* a trivial exercise. – Jesper Juhl Feb 10 '20 at 15:18
  • 1
    Did you know your embedded platform has a C++ compiler? – user253751 Feb 10 '20 at 15:32

2 Answers2

3

This template function cannot be replaced with macro because C preprocessor cannot deduce width and depth values. The best I can suggest is moving part of the code into a C macro, and add a C++ template wrapper on top of that. This will keep the compatibility with existing C++ code, but the C code will have to pass width and depth manually:

// C header
#define crelu(arr, layermap, width, depth) do { \
        for (int i=0;i<width;i++) \
            for(int j=0;j<width;j++) \
                for(int k=0;k<depth;k++) \
                    layermap[i][j][k]=((arr[i][j][k]>0)? arr[i][j][k]:0); \
    } while(0)
// c++ header
template <typename T,int width,int depth>
void relu(T arr[][width][depth],T layermap[][width][depth]) {
    crelu(arr, layermap, width, depth);
}
PooSH
  • 601
  • 4
  • 11
  • Use VLAs: `void relu_int(int width, int depth, int arr[][width][depth], int layermap[][width][depth] )` See https://stackoverflow.com/questions/31870266/passing-a-variable-length-2d-array-to-a-function – Andrew Henle Feb 10 '20 at 15:18
  • @AndrewHenle VLA can be used if we trade `T` for `int` (or write many overloads) – PooSH Feb 10 '20 at 15:20
2

A function template is not a function. A function cannot do what a function template does. A collection of functions can do what a function template does (to some degree; a collection of functions lack template argument deduction, but that's just syntactic sugar). A strict way to implement a comparable set of functions in C is this:

void relu_float_1_1 (float arr[][1][1],  float layermap[][1][1]);
void relu_float_1_2 (float arr[][1][2],  float layermap[][1][2]);
// ... other sizes
void relu_double_1_1(double arr[][1][1], double layermap[][1][1]);
void relu_double_1_2(double arr[][1][2], double layermap[][1][2]);
// ... other sizes
// ... other types

You only need to write the ones that you use.


That said, you don't typically write functions like this in C. Direct transliteration rarely has good results if the goal is to translate one language to another.

A more C style version is to pass the dimensions at runtime:

void relu_float (int width, int depth, float       arr[][width][depth],
                                       float  layermap[][width][depth]);
void relu_double(int width, int depth, double      arr[][width][depth],
                                       double layermap[][width][depth]);
// ... other types
eerorika
  • 232,697
  • 12
  • 197
  • 326