Good day. I want to tell the respected habrachitel one interesting thing that had to face in recent times. In one of the tasks facing me, it was required to implement a class. Some calculations must be performed within this class. For simplicity, as well as in order to preserve the health of the main code, it was decided to use threads. But it turned out to be not so trivial.
The original version was:
//
struct ArgsThread
{
int *tmp1;
char *tmp2;
int indata1;
char indata2;
};
//
unsigned long CalculationThread( void *arg)
{
ThreadArgs* args = reinterpret_cast <ThreadArgs*>(arg); //
// -
}
//
class MyCalc
{
private :
void *HandleThread;
unsigned long IdThread;
int tmp1;
char tmp2;
public :
MyCalc( int indata1, char indata2)
{
ArgsThread* args= new ArgsThread();
args->tmp1 = &tmp1; //
args->tmp2 = &tmp2;
args->indata1 = indata1;
args->indata2 = indata2;
HandleThread = CreateThread(NULL, 0, &CalculationThread, args, 0, &IdThread); //
};
~MyCalc
{
TerminateThread(HandleThreade, NULL); //
CloseHandle(HandleThread); //
};
};
The flow was outside the class, which, in principle, satisfied the task. After deliberation, the idea came to implement the flow within the class, or rather, one of the methods should become a function that the thread would perform.
Without thinking twice (which is very vain), the following code was born:
class MyCalc
{
private :
void *HandleThread;
unsigned long IdThread;
int tmp1;
char tmp2;
protected :
unsigned long CalculationThread( void *arg)
{
// -
}
public :
MyCalc( int indata1, char indata2)
{
HandleThread = CreateThread(NULL, 0, &CalculationThread, this , 0, &IdThread); //
};
~MyCalc
{
TerminateThread(HandleThreade, NULL); //
CloseHandle(HandleThread); //
};
};
Naturally, during the compilation, I received an error stating that the parameters passed to the
CreateThread function are not correct, and in particular the prototype of the stream function did not match the requested type. On one of the forums, a solution was found, the essence of which was that we make the method
static . Which, in principle, also satisfied me, but as it turned out, the stream did not have access to the internal data of the class. And in the end it was decided to make this method a full member of the class. After half an hour of messing around with the compiler, this solution was born:
//
typedef unsigned long ( __stdcall *ThrdFunc)( void *arg); //
typedef unsigned long ( __closure *ClassMethod)( void *arg); //
//
typedef union
{
ThrdFunc Function;
ClassMethod Method;
}tThrdAddr;
//
typedef struct
{
void * Handle; //
tThrdAddr Addr; //
unsigned long Id; // ID
unsigned long ExitCode; //
}tThrd;
class MyCalc
{
private :
tThrd MyThread;
protected :
unsigned long ThrdHandle( void *arg)
{
// -
};
public :
MyCalc()
{
MyThread.Addr.Method = &ThrdHandle; //
MyThread.Handle = CreateThread(NULL, 0, MyThread.Addr.Function, this , 0, &MyThread.Id);
GetExitCodeThread(MyThread.Handle, &MyThread.ExitCode);
};
~MyCalc()
{
if (MyThread.Handle)
{
TerminateThread(MyThread.Handle, MyThread.ExitCode);
CloseHandle(MyThread.Handle);
}
};
};
Thus, our thread becomes a member of the class at the same time, with all the resulting OOP capabilities. There is no need for a “garden” from packing and unpacking the arguments containing the data, since now there is access to all methods and fields of the class. There are infinitely many such flow methods, and the user of the class will not be able to access them at all (
private and
protected rules) or accidentally trigger the execution of the code contained in the flow method.
PS If desired, the prototype of the method may be fundamentally different from what is declared in the example, because only its address is used, but do not forget about the transmitted parameters.
UPD: this code was generated and perfected in the C ++ Builder environment, so
__closure is in the prototype of the method. By changing the prototype, you can use this code in other compilers without large losses and changes.