📜 ⬆️ ⬇️

Wrapper for calling functions at their address

Good day!
It was the case - I did the interface for working with modules for USB from FTDI . I had to tinker with the connection of the DLL interface. Disappointed with the capabilities of the Microsoft Visual Studio 2008 automatic linking ( UPD : then I figured out this topic), I decided to do it manually. Along the way, I got very tired of manually connecting several dozen functions. And then I turned to Google, C ++ and templates. And if the connection of the DLL in the C ++ style did not cause any questions, then the convenient call of the connected functions in the style “Error = FT_Open (Num, & _Handler)”, where the FT_Open is an object, did not work out right away. The result (for such functions) is under the cut. In short - I made a wrapper around the function pointer.

Formulation of the problem


Immediately make a reservation - I work in Windows XP Prof, Visual Studio. This is crucial for getting the function address. However, when working with function pointers, it doesn't matter.

Well, for those who are not in the subject, here is the sequence for finding the very function FT_Open from FTD2XX.dll using WinAPI:

#include "FTD2XX.h" //   FTDI typedef FT_STATUS (*pFT_Open) (int, FT_HANDLE *); //   " FT_OPEN" // ... HMODULE hMod = LoadLibrary ("FTD2XX.dll"); //   - . .   pFT_Open pOpen = GetProcAddress (hMod, "FT_Open"); //    -  . .   // ... FT_STATUS st = pOpen (0, &hDev); //   // ... FreeLibrary (hMod); //   

')
It does not matter when you have one function, but in this very library I counted 51 functions. And for each I need to do the following:

 typedef FT_STATUS (*pFT_Open) (int, FT_HANDLE *); //   "  " pFT_Open pOpen; //  "  " pFT_Open pOpen = GetProcAddress (hMod, "FT_Open"); //    "FT_Open" 


Especially annoying is the need to generate a bunch of typedef. Yes, I know, you can write without typedef, but it will look EXTREMELY!

Therefore, I want to somehow simplify my life:

 Funct2<FT_STATUS, int, FT_HANDLE *> Open; //   " 2 " Open = GetProcAddress (hMod, "FT_Open"); //    "FT_Open" // ... FT_STATUS st = Open (0, &hDev); //   


Decision



During experiments and boiling brain, I got this class template:

 template <typename Ret, typename Arg1, typename Arg2, typename Except = FunctPtrExceptionType, Except Value = FunctPtrExceptionDefValue> class Funct2 { public: typedef Ret (*tfPtr) (Arg1, Arg2); tfPtr fPtr; public: Funct2 (tfPtr Ptr = 0): fPtr (Ptr) {} Funct2 &operator= (void *Ptr) { fPtr = reinterpret_cast<tfPtr> (Ptr); return *this; } Ret operator () (Arg1 A1, Arg2 A2) throw (Except) { if (!fPtr) throw Except (Value); return fPtr (A1, A2); } }; // class Funct2 


I think everything is elementary, but I will explain it to the uninitiated.

A template Funct2 is created, to which the first parameter is the type Ret, returned by the function. The next two parameters - Arg1 and Arg2 - define the types of the function arguments. In order to universalize error handling, the Except exception type is set and its Value is (default parameters are #define FunctPtrExceptionType and #define FunctPtrExceptionDefValue).

In the body of the class template, the type tfPtr “pointer to a function with two parameters” and the pointer fPtr are specified.

The default constructor sets a null pointer or a specific address, if specified. Also the address can be set via overloaded operator = (void * Ptr). Why void * - because GetProcAddress () returns exactly void *. There is no need to overload it with the signature operator = (tfPtr Ptr) - the compiler understands what it is about.

Well, and finally, by overloading operator (), we achieve the use of a class as a functor, and for a class user — as well as a simple function call.

Conveniently? Highly! See:

Result



 #include < Windows.h > //  GetProcAdress #include < stdio.h > //  printf // ** // **     // ** #define FunctPtrExceptionType int //       #define FunctPtrExceptionDefValue 0 //     // ** // **      // ** template < typename Ret = void, typename Except = FunctPtrExceptionType, Except Value = FunctPtrExceptionDefValue > class Funct0 { public: typedef Ret (*tfPtr) (void); tfPtr fPtr; public: Funct0 (tfPtr Ptr = 0): fPtr (Ptr) {} Funct0 &operator= (tfPtr Ptr) { fPtr = Ptr; return this; } Ret operator () (void) throw (Except) { if (!fPtr) throw Except (Value); return fPtr (); } }; // ** // **     1  // ** template < typename Ret, typename Arg1, typename Except = FunctPtrExceptionType, Except Value = FunctPtrExceptionDefValue > class Funct1 { public: typedef Ret (*tfPtr) (Arg1); tfPtr fPtr; public: Funct1 (tfPtr Ptr = 0): fPtr (Ptr) {} Funct1 &operator= (void *Ptr) { fPtr = reinterpret_cast<tfPtr> (Ptr); return *this; } Ret operator () (Arg1 A1) throw (Except) { if (!fPtr) throw Except (Value); return fPtr (A1); } }; // ** // **     2  // ** template < typename Ret, typename Arg1, typename Arg2, typename Except = FunctPtrExceptionType, Except Value = FunctPtrExceptionDefValue > class Funct2 { public: typedef Ret (*tfPtr) (Arg1, Arg2); tfPtr fPtr; public: Funct2 (tfPtr Ptr = 0): fPtr (Ptr) {} Funct2 &operator= (void *Ptr) { fPtr = reinterpret_cast<tfPtr> (Ptr); return *this; } Ret operator () (Arg1 A1, Arg2 A2) throw (Except) { if (!fPtr) throw Except (Value); return fPtr (A1, A2); } }; // ** // **    // ** int add (const int A, const int *B) { int C; C = A + *B; printf (" int add (const int %d, const int %d) = %d\n", A, *B, C); return C; } void prn (void) { printf (" void prn (void)\n"); } // ** // **   // ** void main (void) { int i, i2; double d; long l; Funct0<> prner (prn); Funct1< double, long *, int, 2 > longer; Funct2< int, const int, const int * > adder; adder = add; longer = GetProcAddress (0, "Longer"); try { prner (); i2 = 6; i = adder (5, &i2); d = longer (&l); } catch (int val) { switch (val) { case 2: printf (" ***     !\n"); break; default: printf (" ***   !\n"); break; } } 


Total



The disassembler in Release mode showed that the overhead when calling such a function is to check the 0th value and, in this regard, another call. I think for modern PCs it doesn't matter.

For perfection, you can somehow refine the topic of exceptions - it would be good to send text strings, your own arbitrary error classes, etc. But I am too lazy to know patterns well enough to implement it.

Well, of course, you need to rivet different versions of Funct for 3x, 4x, etc. arguments. It would be nice to come up with some kind of macro that would generate them ...

Well and, even more understandably, you need to put all this in a separate .H file.

I hope someone saved time. I would be grateful for constructive comments!

PS In the course of operation, such an unpleasant thing as a call agreement was revealed. Looks like you have to do Funct0Stdcall, Funct0Cdecl; Funct1Stdcall, Funct1Cdecl ...

Source: https://habr.com/ru/post/111680/


All Articles