0

I am trying to call a C file (dispmanx.c) from a C++ file (main.cpp).

dispmanx.c :

#ifdef __cplusplus
extern "C" {
#endif

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
#include <unistd.h>
#include <sys/time.h>

#include "bcm_host.h"

#define WIDTH   200
#define HEIGHT  200

#ifndef ALIGN_UP
#define ALIGN_UP(x,y)  ((x + (y)-1) & ~((y)-1))
#endif

int run(unsigned char* fileData)
{
    typedef struct
    {
        DISPMANX_DISPLAY_HANDLE_T   display;
        DISPMANX_MODEINFO_T         info;
        void                       *image;
        DISPMANX_UPDATE_HANDLE_T    update;
        DISPMANX_RESOURCE_HANDLE_T  resource;
        DISPMANX_ELEMENT_HANDLE_T   element;
        uint32_t                    vc_image_ptr;

    } RECT_VARS_T;

    static RECT_VARS_T  gRectVars;


    RECT_VARS_T    *vars;
    uint32_t        screen = 1;
    int             ret;
    VC_RECT_T       src_rect;
    VC_RECT_T       dst_rect;
    VC_IMAGE_TYPE_T type = VC_IMAGE_RGB565;
    int width = WIDTH, height = HEIGHT;
    int pitch = ALIGN_UP(width*2, 32);
    int aligned_height = ALIGN_UP(height, 16);
    VC_DISPMANX_ALPHA_T alpha = { DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS, 
                             120, /*alpha 0->255*/
                             0 };

    vars = &gRectVars;

    bcm_host_init();

    printf("Open display[%i]...\n", screen );
    vars->display = vc_dispmanx_display_open( screen );

    ret = vc_dispmanx_display_get_info( vars->display, &vars->info);
    assert(ret == 0);
    printf( "Display is %d x %d\n", vars->info.width, vars->info.height );

    vars->image = fileData;

//  vars->image = calloc(1, pitch * height);
    assert(vars->image);

    vars->resource = vc_dispmanx_resource_create( type,
                                                  width,
                                                  height,
                                                  &vars->vc_image_ptr );
    assert( vars->resource );
    vc_dispmanx_rect_set( &dst_rect, 0, 0, width, height);
    ret = vc_dispmanx_resource_write_data(  vars->resource,
                                            type,
                                            pitch,
                                            vars->image,
                                            &dst_rect );


    assert( ret == 0 );
    vars->update = vc_dispmanx_update_start( 10 );
    assert( vars->update );

    vc_dispmanx_rect_set( &src_rect, 0, 0, width << 16, height << 16 );

    vc_dispmanx_rect_set( &dst_rect, ( vars->info.width - width ) / 2,
                                     ( vars->info.height - height ) / 2,
                                     width,
                                     height );

    vars->element = vc_dispmanx_element_add(    vars->update,
                                                vars->display,
                                                2000,               // layer
                                                &dst_rect,
                                                vars->resource,
                                                &src_rect,
                                                DISPMANX_PROTECTION_NONE,
                                                &alpha,
                                                NULL,             // clamp
                                                VC_IMAGE_ROT0 );

    ret = vc_dispmanx_update_submit_sync( vars->update );
    assert( ret == 0 );

    printf( "Sleeping for 10 seconds...\n" );
    sleep( 10 );

    vars->update = vc_dispmanx_update_start( 10 );
    assert( vars->update );
    ret = vc_dispmanx_element_remove( vars->update, vars->element );
    assert( ret == 0 );
    ret = vc_dispmanx_update_submit_sync( vars->update );
    assert( ret == 0 );
    ret = vc_dispmanx_resource_delete( vars->resource );
    assert( ret == 0 );
    ret = vc_dispmanx_display_close( vars->display );
    assert( ret == 0 );

    return 0;
}

#ifdef __cplusplus
}
#endif

the main.cpp:

#include "dispmanx.c"


#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>

using namespace cv;
using namespace std;
int main()
{
    Mat image;
    image = imread("t.png", IMREAD_COLOR);   // Read the file
    run(image.data);
}

the code should take an address of a png file get the data of it using OpenCV libraries and pass it to the C program which shows the picture on the screen.

I could compile the dispmanx.c:

pi@raspberrypi:~/openCVtest.1 $ gcc -I/opt/vc/include/
    -I/opt/vc/include/interface/vcos/pthreads
    -I/opt/vc/include/interface/vmcs_host/linux
    -I/opt/vc/src/hello_pi/libs/ilclient
    -I/opt/vc/src/hello_pi/libs/vgfont
    -L/opt/vc/lib/ -L/opt/vc/src/hello_pi/libs/ilclient
    -L/opt/vc/src/hello_pi/libs/vgfont
    -c dispmanx.c
    -o dispmanx.o
    -lbrcmGLESv2 -lbrcmEGL -lopenmaxil -lbcm_host -lvcos -lvchiq_arm -lpthread -lrt -lm

but when I try to compile the main.cpp:

pi@raspberrypi:~/openCVtest.1 $ g++ -I/opt/vc/include/     -I/opt/vc/include/interface/vcos/pthreads -I/opt/vc/include/interface/vmcs_host/linux -I/opt/vc/src/hello_pi/libs/ilclient -I/opt/vc/src/hello_pi/libs/vgfont -L/opt/vc/lib/ -L/opt/vc/src/hello_pi/libs/ilclient -L/opt/vc/src/hello_pi/libs/vgfont -c main.cpp dispmanx.o  -o main.o -lbrcmGLESv2 -lbrcmEGL -lopenmaxil -lbcm_host -lvcos -lvchiq_arm -lpthread -lrt -lm               In file included from main.cpp:2:0:

dispmanx.c: In function ‘int run(unsigned char*)’: dispmanx.c:52:68: error: invalid conversion from ‘int’ to ‘DISPMANX_FLAGS_ALPHA_T’ [-fpermissive] VC_DISPMANX_ALPHA_T alpha = { DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS, ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

dispmanx.c:111:63: error: cannot convert ‘VC_IMAGE_TRANSFORM_T’ to ‘DISPMANX_TRANSFORM_T’ for argument ‘10’ to ‘DISPMANX_ELEMENT_HANDLE_T vc_dispmanx_element_add(DISPMANX_UPDATE_HANDLE_T, DISPMANX_DISPLAY_HANDLE_T, int32_t, const VC_RECT_T*, DISPMANX_RESOURCE_HANDLE_T, const VC_RECT_T*, DISPMANX_PROTECTION_T, VC_DISPMANX_ALPHA_T*, DISPMANX_CLAMP_T*, DISPMANX_TRANSFORM_T)’
                                             VC_IMAGE_ROT0 );

I have tested both program separately and the function as they should, however when I tried to mix them I get errors that I cant understand.

I am a student have beginner level of knowledge. what am I doing wrong?

Paul Ogilvie
  • 25,048
  • 4
  • 23
  • 41
Ben
  • 13
  • 3
  • You might want to show where line 111 in `dispmanx.c` is. It's possible to count to 111 and find it, but better make it easier to readers by marking **// The error is here** or something like that. You can [edit] your question to do it. – anatolyg Aug 16 '18 at 10:33

2 Answers2

1

Given these contents of dispmanx.c:

#ifdef __cplusplus
extern "C" {
#endif

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
#include <unistd.h>
#include <sys/time.h>

and main.cpp starts with

#include "dispmanx.c"

You are going to have serious problems.

You've wrapped entire standard header files such as unistd.h with extern "C", and then #include'd them in a C++ file.

They're not meant to be used that way in C++ code.

#include'ing a source file (.c, .cpp, etc) is a fundamentally bad idea in the first place. Doing it across different languages such a C and C++ and then improperly wrapping system headers with extern "C" is even worse. You can't safely compile C code as C++ code, and you certainly can't wrap system header files with extern "C" for use in C++ code.

extern "C" does not make it safe to compile C code with a C++ compiler. They are different languages, with subtle distinctions.

Andrew Henle
  • 32,625
  • 3
  • 24
  • 56
  • In fairness, those are all C headers and possibly have `#ifdef`/`extern "C"` combos of their own – Lightness Races in Orbit Aug 16 '18 at 10:57
  • Thank you very useful info, if wrapping with extern "c" is not a good approach what do you suggest for a fix to my problem? would it be a better option if i create a main method for the dispmanx.c and tried a wrapper for my cpp code(which contains OpenCV libraries)? – Ben Aug 16 '18 at 11:12
  • 1
    @LightnessRacesinOrbit But the [Linux glibc `stdio.h` header](https://code.woboq.org/userspace/glibc/libio/stdio.h.html) starts with the comment `Define ISO C stdio on top of C++ iostreams.` I'm not going through all that trying to figure out what wrapping something like that with `extern "C"` does when included in a C++ source file. – Andrew Henle Aug 16 '18 at 11:18
  • @AndrewHenle I don't know what that comment's doing there, but it's implausible that glibc I/O is a wrapper around the C++ standard library's IOstreams. – Lightness Races in Orbit Aug 16 '18 at 11:19
  • @Ben You compile your C code with a C compiler, your C++ code with a C++ compiler, than you can link them together. See https://stackoverflow.com/questions/31903005/how-to-mix-c-and-c-correctly for an example. – Andrew Henle Aug 16 '18 at 11:19
  • @LightnessRacesinOrbit I don't know what it's doing there either, but that file **isn't** wrapping itself with `#ifdef __cplusplus extern "C"`, and it starts off by including 8 or 9 other header files, making for a big pile of code from all over getting pulled in. I'd think there's a really good chance that wrapping it with `extern "C"` when including it in C++ code breaks something, but I don't plan on digging through all that to be sure. – Andrew Henle Aug 16 '18 at 11:24
  • @AndrewHenle I did say "possibly" ;) Note that I am not disagreeing with your answer – Lightness Races in Orbit Aug 16 '18 at 11:28
1

The problem is here:

#include "dispmanx.c"

Including a *.c file in a *.cpp file is not a good idea. C and C++ are similar languages, so it may look that it works at first (i.e. the compiler reports the error not at line 1 but at line 111), but actually it doesn't.

One correct way to call C code from C++ code is by providing a declaration like this:

// C++ code
#include <iostream>

extern "C" {
    int run(unsigned char* fileData); // declaration for the C code
}

...
int main()
{
    ...
    run(image.data); // calling the C code
}

The declaration for the C code can be in the *.cpp file or, more conventionally, in a separate dedicated *.h file. If your C code is in dispmanx.c, then a conventional name is dispmanx.h:

// dispmanx.h
extern "C" {
    int run(unsigned char* fileData); // declaration for the C code
}
// main.cpp
#include <iostream>
#include "dispmanx.h"
...
int main()
{
    ...
    run(image.data); // calling the C code
}

Also, another convention: if you want to make your new dispmanx.h file compilable in both C and C++, you should hide the extern "C" part from the C compiler, as described in this question and answer.

anatolyg
  • 26,506
  • 9
  • 60
  • 134