I am using Microsoft Visual Studio 2010 and the OpenCV 2.3.0 libraries to write up an application for image processing.
I have a piece of code that is erroneous for me and I am not sure how to fix it. I am implementing an application where there will be 2 or 3 windows open at the same time and I want each one of them to be assigned with a different CvMouseCallback function. I want all these CvMouseCallback functions to be in a different class together with another function that returns a pointer to one of these functions according to what the user selects.
My Window.h contains this piece of code.
class Window
{
public:
... // constructors and destructors
void setMouseHandler( CvMouseCallback mouseHandler );
private:
... // other stuff
};
and Window.cpp
#include "stdafx.h"
void Window::setMouseHandler( CvMouseCallback mouseHandler )
{
cvSetMouseCallback( win, mouseHandler, NULL );
}
Now, the MouseHandler.h file
class MouseHandler
{
public:
...
CvMouseCallback selectHandler( int option );
void __cdecl selectROI( int event, int x, int y, int flags, void *param );
private:
Image *in;
Window *win;
void ( CV_CDECL MouseHandler::*callback )( int event, int x, int y, int flags, void *param );
};
and lastly, in MouseHandler.cpp I contain
void __cdecl MouseHandler::selectROI( int event, int x, int y, int flags, void *param )
{
//do something
}
CvMouseCallback MouseHandler::selectHandler( int option )
{
callback = (MouseHandler::selectROI);
return callback;
}
The last bit of information you might need is the definition of CvMouseCallback from the OpenCV library which is
typedef void (CV_CDECL *CvMouseCallback )(int event, int x, int y, int flags, void* param);
Now, the question is: When I return the callback from the last function in MouseHandler.cpp it is underlined with an error saying:
Error: return value type does not match the function type.
I know what it says is that I am trying to impose to that function to return something that does not look like the object it is being asking for. However, it's just a function and if I could do that in the main class it would be ok. My problem is how can selectHandler return a pointer to the selectROI function so that it can be used by another class?
I think that you need to use a static function here:
static void __cdecl selectROI( int event, int x, int y, int flags, void *param );
and
void ( CV_CDECL *callback )( int event, int x, int y, int flags, void *param );
accordingly.
The thing is that this definition:
typedef void (CV_CDECL *CvMouseCallback )(int event, int x, int y, int flags, void* param);
Is not a class member function, while yours is a member of class MouseHandler, which means its a different signature and different parameter list (to accommodate for this). Using static class member function solves it for you.
You'll have to figure out how to pass the object context data to the static function of course.
Your selectROI() method, since it is not static, requires an implicit this parameter as its first argument. If you try making it static, you will have a better chance at getting it working, though really if you are passing it to a C API as you are, you technically need to pass an extern "C" function pointer for everything to be fully proper and portable. That might be a trivial forwarding function like this:
extern "C" void selectROI( int event, int x, int y, int flags, void *param );
Then, if you want your C++ class method to not be static (so it can access class member variables), you just need to pass a pointer to a MouseHandler object as the third argument to cvSetMouseCallback() and you will then receive the same in your callback, which can then look like this:
extern "C" void selectROI( int event, int x, int y, int flags, void *param )
{
static_cast<MouseHandler*>(param)->selectROI( event, x, y, flags);
}
Related
I'm new to ffi. But I successfully used dart-ffi with functions call.
Now, I'd like to use a C++ object in dart ffi. I don't know if it is possible, but I tried like this.
The prototypes of constructor call are :
function_dart = lib
.lookup<NativeFunction<function_native>>("constructor_function")
.asFunction();
But I've got :
Failed to lookup symbol <constructor_function>, where I tried constructor function with :
constructor_function
class::constructor_function
class::constructor_function(args)
I did nm -gDC <lib>, and I can see the constructor.
Help !
edit 1 : #Botje, #Richard-Heap
I'm trying to use the VideoCapture instance from OpenCV.
I have followed the instructions from Botje's answer.
So I created a lib, like this :
bind.hpp :
#ifndef BIND_HPP
# define BIND_HPP
#include <opencv2/videoio.hpp>
extern "C" {
cv::VideoCapture *cvCreateVideoCapture(char *filename, int apiPreference);
}
#endif
bind.cpp :
#include "bind.hpp"
cv::VideoCapture *createVideoCapture(char *filename, int apiPreference) {
return new cv::VideoCapture(filename, apiPreference);
}
The commands I use to compile :
g++ -c bind.cpp -lopencv -o bind.o
g++ bind.o -shared -o bind.so
I get : dart: symbol lookup error: ./lib/src/bind.so: undefined symbol: _ZN2cv12VideoCaptureC1ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEi
Next step, is to use a method of VideoCapture instance.
Thank you
Dart ffi uses a C interface, so you have to adapt as follows.
Start with C++ class
Rect::Rect(int32_t width, int32_t height) {
m_width = width;
m_height = height;
}
void Rect::setWidth(int32_t width) {
m_width = width;
}
void Rect::setHeight(int32_t height) {
m_height = height;
}
int32_t Rect::area() {
return m_width * m_height;
}
create a C adapter header
EXTERNC void* rect_init(int32_t width, int32_t height);
EXTERNC void rect_destroy(void *ptr);
EXTERNC int32_t rect_area(void *ptr);
and implementation
void* rect_init(int32_t width, int32_t height){
return new Rect(width, height);
}
void rect_destroy(void *ptr){
auto typed_ptr = static_cast<Rect*>(ptr);
delete typed_ptr;
}
int32_t rect_area(void *ptr){
auto typed_ptr = static_cast<Rect*>(ptr);
return typed_ptr->area();
}
in Dart, create the typedefs
typedef example_init_rect = Pointer<Void> Function(Int32 w, Int32 h);
typedef ExampleInitRect = Pointer<Void> Function(int w, int h);
typedef example_free_rect = Void Function(Pointer<Void> p);
typedef ExampleFreeRect = void Function(Pointer<Void> p);
typedef example_area_rect = Int32 Function(Pointer<Void> p);
typedef ExampleAreaRect = int Function(Pointer<Void> p);
and bind the C adapter functions. Finally, you could create a Dart class that proxies the underlying C++ class.
class NativeRect {
Pointer<Void> _nativeInstance;
NativeRect(int width, int height) {
_nativeInstance = Example()._exInitRect(width, height);
}
void free() {
Example()._exFreeRect(_nativeInstance);
}
int get area => Example()._exAreaRect(_nativeInstance);
}
C++ compilers use "name mangling" to ensure symbol names are unique. The fact that you had to add the -C option (or --demangle) to make it show up is a hint.
For example, here is the mangled symbol for some_class::some_class(int, std::string):
_ZN10some_classC2EiNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
You will need to pass the mangled name (not the demangled name) in order to call the constructor. You will also need to match the ABI for an object (ie have a pointer to the object's memory in the correct register). Sometimes this is simply a hidden first argument to the constructor, but not in all ABIs.
If at all possible, write a C++ wrapper function that constructs the object for you and tag it with extern "C" so you don't have to jump through these hoops.
For example:
extern "C"
some_class* create_some_class(int x, const char * some_text) {
return new some_class(x, some_text);
}
You can now simply call create_some_class from Dart with basic types and you will get back a pointer to the constructed C++ object.
If you intend to wrap a large API like this, consider migrating to something like SWIG that can auto-generate these wrappers.
I have my class:
class Foo
{
public:
(...)
private:
void mycallback(void* buff, wifi_promiscuous_pkt_type_t type);
void registerMyCallback();
};
The mycallback is the callback.
I want to use a method esp_wifi_set_promiscuous_rx_cb to register the mycallback so that when a WiFi packet is detected, this callback method will be executed.
The esp_wifi_set_promiscuous_rx_cb signature is:
esp_err_t esp_wifi_set_promiscuous_rx_cb(wifi_promiscuous_cb_t cb);
Where the wifi_promiscuous_cb_t definition is:
typedef void (* wifi_promiscuous_cb_t)(void *buf, wifi_promiscuous_pkt_type_t type);
I want to use the mycallback method inside my class, therefore I simply can't use like this:
void Foo::registerMyCallback()
{
esp_wifi_set_promiscuous_rx_cb(&mycallback);
}
I know that I could use something similar if I would just make my method as static.
Is there anyway that I bind mycallback to esp_wifi_set_promiscuous_rx_cb without making the callback static?
I have tried the following:
esp_wifi_set_promiscuous_rx_cb(std::bind(&Foo::mycallback, this, std::placeholders::_1, std::placeholders::_2));
But I am still having the following error:
cannot convert 'std::_Bind_helper<false, void (Foo::Foo::*)(void*, wifi_promiscuous_pkt_type_t),
Foo::Foo*, const std::_Placeholder<1>&, const std::_Placeholder<2>&>::type
to
'wifi_promiscuous_cb_t {aka void (*)(void*, wifi_promiscuous_pkt_type_t)}' for argument '1'
Th library you are using is C package.
Thus the only guaranteed way pass a valid function is to pass a C function with C linkage. This function can then call the method on your object.
If you want the callback method to be non static you need to store a pointer (ore reference) to the callback object somewhere that your callback function can find it. (in most C callback functions you can provide a void* object that is passed to your callback, but this interface does not seem to allow this so you will have to save the value yourself).
Foo* myCBObject = nullptr;
extern "C" void myCB(void *buf, wifi_promiscuous_pkt_type_t type)
{
try
{
myCBObject->mycallback(buff, type);
}
catch(...) {} // Don't allow exceptions to cross C linkage
}
...
// Your code.
void Foo::registerMyCallback()
{
myCBObject = this;
esp_wifi_set_promiscuous_rx_cb(myCB);
}
Note: You should NOT be registering static member functions with a C library. If this works it is only by chance. There is no guarantee that a static function has the same calling convention of a C function (they usually do but that is not guaranteed).
After some research, I hope I found the solution. The trick is to bind member function first and then obtain the function pointer from the std::function. Notice the usage of my_wifi_promiscuous_cb_t and std::function::target<>().
#include <iostream>
#include <functional>
using namespace std::placeholders;
// using fake definitions
extern "C"
{
enum wifi_promiscuous_pkt_type_t {};
typedef int32_t esp_err_t;
typedef void (*wifi_promiscuous_cb_t)(void* buf, wifi_promiscuous_pkt_type_t type);
typedef void my_wifi_promiscuous_cb_t(void* buf, wifi_promiscuous_pkt_type_t type);
esp_err_t esp_wifi_set_promiscuous_rx_cb(wifi_promiscuous_cb_t cb)
{
return 0;
}
}
class Class
{
public:
void mycallback(void* buff, wifi_promiscuous_pkt_type_t type) {}
void registerMyCallback() {
std::function<void(void*, wifi_promiscuous_pkt_type_t)> fun2 = std::bind(&Class::mycallback, this, _1, _2);
esp_wifi_set_promiscuous_rx_cb(fun2.target<my_wifi_promiscuous_cb_t>());
}
};
int main()
{
Class c;
c.registerMyCallback();
}
I'm new to ffi. But I successfully used dart-ffi with functions call.
Now, I'd like to use a C++ object in dart ffi. I don't know if it is possible, but I tried like this.
The prototypes of constructor call are :
function_dart = lib
.lookup<NativeFunction<function_native>>("constructor_function")
.asFunction();
But I've got :
Failed to lookup symbol <constructor_function>, where I tried constructor function with :
constructor_function
class::constructor_function
class::constructor_function(args)
I did nm -gDC <lib>, and I can see the constructor.
Help !
edit 1 : #Botje, #Richard-Heap
I'm trying to use the VideoCapture instance from OpenCV.
I have followed the instructions from Botje's answer.
So I created a lib, like this :
bind.hpp :
#ifndef BIND_HPP
# define BIND_HPP
#include <opencv2/videoio.hpp>
extern "C" {
cv::VideoCapture *cvCreateVideoCapture(char *filename, int apiPreference);
}
#endif
bind.cpp :
#include "bind.hpp"
cv::VideoCapture *createVideoCapture(char *filename, int apiPreference) {
return new cv::VideoCapture(filename, apiPreference);
}
The commands I use to compile :
g++ -c bind.cpp -lopencv -o bind.o
g++ bind.o -shared -o bind.so
I get : dart: symbol lookup error: ./lib/src/bind.so: undefined symbol: _ZN2cv12VideoCaptureC1ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEi
Next step, is to use a method of VideoCapture instance.
Thank you
Dart ffi uses a C interface, so you have to adapt as follows.
Start with C++ class
Rect::Rect(int32_t width, int32_t height) {
m_width = width;
m_height = height;
}
void Rect::setWidth(int32_t width) {
m_width = width;
}
void Rect::setHeight(int32_t height) {
m_height = height;
}
int32_t Rect::area() {
return m_width * m_height;
}
create a C adapter header
EXTERNC void* rect_init(int32_t width, int32_t height);
EXTERNC void rect_destroy(void *ptr);
EXTERNC int32_t rect_area(void *ptr);
and implementation
void* rect_init(int32_t width, int32_t height){
return new Rect(width, height);
}
void rect_destroy(void *ptr){
auto typed_ptr = static_cast<Rect*>(ptr);
delete typed_ptr;
}
int32_t rect_area(void *ptr){
auto typed_ptr = static_cast<Rect*>(ptr);
return typed_ptr->area();
}
in Dart, create the typedefs
typedef example_init_rect = Pointer<Void> Function(Int32 w, Int32 h);
typedef ExampleInitRect = Pointer<Void> Function(int w, int h);
typedef example_free_rect = Void Function(Pointer<Void> p);
typedef ExampleFreeRect = void Function(Pointer<Void> p);
typedef example_area_rect = Int32 Function(Pointer<Void> p);
typedef ExampleAreaRect = int Function(Pointer<Void> p);
and bind the C adapter functions. Finally, you could create a Dart class that proxies the underlying C++ class.
class NativeRect {
Pointer<Void> _nativeInstance;
NativeRect(int width, int height) {
_nativeInstance = Example()._exInitRect(width, height);
}
void free() {
Example()._exFreeRect(_nativeInstance);
}
int get area => Example()._exAreaRect(_nativeInstance);
}
C++ compilers use "name mangling" to ensure symbol names are unique. The fact that you had to add the -C option (or --demangle) to make it show up is a hint.
For example, here is the mangled symbol for some_class::some_class(int, std::string):
_ZN10some_classC2EiNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
You will need to pass the mangled name (not the demangled name) in order to call the constructor. You will also need to match the ABI for an object (ie have a pointer to the object's memory in the correct register). Sometimes this is simply a hidden first argument to the constructor, but not in all ABIs.
If at all possible, write a C++ wrapper function that constructs the object for you and tag it with extern "C" so you don't have to jump through these hoops.
For example:
extern "C"
some_class* create_some_class(int x, const char * some_text) {
return new some_class(x, some_text);
}
You can now simply call create_some_class from Dart with basic types and you will get back a pointer to the constructed C++ object.
If you intend to wrap a large API like this, consider migrating to something like SWIG that can auto-generate these wrappers.
I have two classes, and this is the header of one of them:
#ifndef WRAPPER_HPP
#define WRAPPER_HPP
#include <SDL/SDL.h>
using namespace std;
class Wrapper
{
private:
//SDL_Surface *screen;
public:
static SDL_Surface *screen;
static void set_screen(SDL_Surface *_screen);
static void set_pixel(int x, int y, Uint8 color);
static void clear_screen(int r, int g, int b);
static SDL_Surface* load_image(char path[500]);
static void draw_image(SDL_Surface *img, int x, int y, int width, int height);
static void draw_line(int x1, int y1, int x2, int y2, Uint8 color);
};
#endif
I am calling Wrapper::set_screen(screen) from another file and I get this error:
In file included from /home/david/src/aships/src/Wrapper.cpp:6:0:
/home/david/src/aships/src/Wrapper.hpp: In static member function ‘static void Wrapper::set_screen(SDL_Surface*)’:
/home/david/src/aships/src/Wrapper.hpp:11:18: error: invalid use of member ‘Wrapper::screen’ in static member function
/home/david/src/aships/src/Wrapper.cpp:10:3: error: from this location
I also get a similar error for the definition of every single function on Wrapper.cpp, for example:
void Wrapper::set_pixel(int x, int y, Uint8 color)
{
/* Draws a pixel on the screen at (x, y) with color 'color' */
Uint8 *p;
p = (Uint8 *) screen->pixels + y * screen->pitch + x * screen->format->BytesPerPixel;
*p = color;
}
On compile:
/home/david/src/aships/src/Wrapper.hpp: In static member function ‘static void Wrapper::set_pixel(int, int, Uint8)’:
/home/david/src/aships/src/Wrapper.hpp:11:18: error: invalid use of member ‘Wrapper::screen’ in static member function
/home/david/src/aships/src/Wrapper.cpp:17:17: error: from this location
I know it's related to the class being static and thus the variable Wrapper.screen is not accessible or something, but I'm not sure of how to fix it. Any ideas?
You are using a static variable
static SDL_Surface *screen;
in your code.
In C++ when you declare a static variable in the .h (or .hpp) you are creating a variable that is general (static) to the class. Thus, to use it in another file you have to redeclare it (which I'm guessing you didn't) to create a variable in that file referencing the static one. In your case put this:
SDL_Surface* Wrapper::screen;
in the .cpp file.
I'm not sure the theory is well explained, but it works like that.
Your class and member (screen) are not static, which means they don't actually exist.
You can't access a non static member in a static function.
Try to make your data members to be static.
I'm not convinced that the code abstract you show us is an accurate characterization of your problem.
Your header should not include using namespace std; — it doesn't use or declare anything from the std namespace, and specifying using namespace std; is generally regarded as 'not a good idea', doubly so when it appears in a header file.
It also isn't clear that your header needs to include SDL/SDL.h. If the Uint8 type is easily isolated (not necessarily valid), then your header file can simply use a forward declaration of the SDL_Surface class. (Your implementation code will need to include SDL/SDL.h; but you should not burden the users of your wrapper class with unnecessary #include directives when simple forward declarations would suffice.)
This code is self-contained (does not need any headers), but more or less simulates what you could use, and it compiles OK:
#ifndef WRAPPER_HPP
#define WRAPPER_HPP
typedef unsigned char Uint8;
class SDL_Surface;
class Wrapper
{
public:
static SDL_Surface *screen;
static void set_screen(SDL_Surface *_screen);
static void set_pixel(int x, int y, Uint8 color);
static void clear_screen(int r, int g, int b);
static SDL_Surface *load_image(char path[500]);
static void draw_image(SDL_Surface *img, int x, int y, int width, int height);
static void draw_line(int x1, int y1, int x2, int y2, Uint8 color);
};
#endif
//#include <SDL/SDL.h>
typedef unsigned short Uint16;
class SDL_Surface
{
public:
Uint8 *pixels;
Uint16 pitch;
struct
{
Uint8 BytesPerPixel;
} *format;
};
// End of SDL/SDL.h
void Wrapper::set_pixel(int x, int y, Uint8 color)
{
/* Draws a pixel on the screen at (x, y) with color 'color' */
Uint8 *p;
p = (Uint8 *) screen->pixels + y * screen->pitch + x * screen->format->BytesPerPixel;
*p = color;
}
It also compiles without warnings. The (Uint8 *) cast (copied from the original) is unnecessary. With the class definition given, it is superfluous; if you are needing to use a cast because the type of the pixels member of SDL_Surface actually isn't Uint8, are you sure it is a good idea? And can't you use reinterpret_cast<Uint8>(screen->pixels) instead to make it clearer?
Can you reduce your problem to code analogous to this that still shows the actual error?
I was tweaking a bit of GDAL code, and am using a typedef like this
typedef CPLErr (*MYWriter)( double dfLevel, int nPoints, double *padfX, double *padfY, void * );
which is being used in a class like this
class GDALGenerator
{
...blah...
public:
MYWriter pfnWriter;
GDALGenerator( int nWidth, int nHeight, MYWriter pfnWriter, void *pWriterCBData );
...blah...
};
but in the same file, below the GDALGenerator class when I create the function like so
CPLErr MYWriter( double dfLevel, int nPoints, double *padfX, double *padfY, void *pInfo )
{}
I get this error
Error 2 error C2365: 'MYWriter' : redefinition; previous definition
was 'typedef' f:\projects\map\somecpp\somecpp.cpp 1330 MyProjectName
I'm confused, because a standard GDAL function is being used exactly like this, and it works fine (the class is in a separate DLL in that case). I just made a copy of the function with the different name, and it doesn't work.
you cannot use the type name as a function name, only as a type of a variable.
I hope this makes it clear:
CPLErr f( double dfLevel, int nPoints, double *padfX, double *padfY, void *pInfo )
{}
MYWriter foo = f;
``