Generic Multithreading (Boost)

Hello all,

Here is my problem: I have 3D containers in which I store any data on which I wish to apply various filters, mathematical morphology, etc. ... So far, to parallelize these operations, I made a function to the operation itself and a function that create my threads and distribute their work (each thread processes a portion of the volume). The thing is this second function is actually the same all the time except the name of the function it calls and parameters of the called function. I seek a way to write a generic function that could call me any function (any name, any number and type of parameters). So far I have come to call any function by passing a function pointer to my generic function but the problem is that all functions that it can potentially call must have the same parameters (which is obviously not the case with my functions). I solved the problem by passing a vector of pointers to the generic function. Then it works fine but I am obliged to make many static_cast at the beginning of my functions for my use pointers and more I find myself manipulating pointers everywhere and it's a bit boring .... I seek a more beautiful way to do this, which keeps me from going through a vector of pointers :)
For threads manipulation, I use Boost::threads.

Thanks :)


PS : Here is my code (functions Run,affiche_test and test_thread are useless) :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#include "My_Functions.hpp"

#ifndef threadbool
	#define threadbool 0
	#ifdef THREAD
	#include <boost/thread/thread.hpp>
	#undef threadbool
	#define threadbool 1
	#endif
#endif

using namespace std;

// ===================================================================

class erode_class{

public:

	erode_class(vector<void*> args_):ptrs_(args_){};

	void Run(vector<void* > &truc){
	cout<<"Parameters :"<<endl;
	for(size_t i=0;i<ptrs_.size();++i){
		cout<<"\t"<<*(static_cast<int *> (ptrs_[i]))<<endl;
		}
	cout<<"Additional parameters :"<<endl;
	for(size_t i=0;i<truc.size();++i){
		int *temp=static_cast<int *> (truc[i]);
		cout<<"\t"<<(*temp)<<endl;
		}
		cout<<endl<<endl;
	};

	template<class T>
	void thread_erode_b(vector<void *> &arguments_,vector<bool> &traites){
	if(arguments_.size()!=5){cout<<"Erode arguments are incorrect"<<endl<<"End program"<<endl;exit(0);}
	T *volume=static_cast<T *> (arguments_[0]);
	T *volume2=static_cast<T *> (arguments_[1]);
	int ordre=*(static_cast<int *> (arguments_[2]));
	int planstart=*(static_cast<int *> (arguments_[3]));
	int planstop=*(static_cast<int *> (arguments_[4]));
	const int dim1=(*volume).dim1(),dim2=(*volume).dim2(),dim3=(*volume).dim3();
	
	// Traitement proprement dit
	
	}
private:
	vector<void*> ptrs_;
};

typedef void (erode_class::*traitement)(vector<void *> &arguments_,vector<bool> &traites);
typedef void (erode_class::*Run_test)(vector<void *> &truc);

// =========================================================================
template <class T>
void GenericThreadedFunction_2Volumes(T &volume1,T &volume2,const size_t nb_threads,vector<void* > &args,erode_class &test,int &planstart,int &planstop,vector<bool> &traites, traitement fonction){
volume2.resize(volume1.dim1(),volume1.dim2(),volume1.dim3());
#if threadbool
boost::thread_group threads1;
size_t box_size=volume1.dim1();

for (std::size_t n=0; n<nb_threads; ++n){
	size_t nb_plans=(int)((double)box_size/(double)nb_threads);
	if(n<box_size%nb_threads)nb_plans++;
	if(n>=box_size%nb_threads){
		planstart=n*nb_plans+box_size%nb_threads;
		}
	else {
		planstart=n*nb_plans;
		}
	planstop=planstart-1+nb_plans;
// I know this is not the optimal way to spread the work but I ll improve it later :)
	args[3]=&planstart;
	args[4]=&planstop;
	cout<<n<<" : "<<*(static_cast<int*> (args[3]))<<" to "<<*(static_cast<int*> (args[4]))<<endl;
	threads1.create_thread(boost::bind(fonction,boost::ref(test),boost::ref(args),boost::ref(traites)));
}
threads1.join_all();
#else
size_t planstart=0,planstop=volume1.dim1()-1;
fonction(volume1,volume2,ordre,planstart,planstop);
#endif
}

template<typename T>
void affiche_test(double i){
cout<<"Affiche_test = "<<T(i)<<endl;
}

template<typename T>
void test_thread(T j,void (*f)(T k)){
boost::thread_group threads1;
for(int i=0;i<2;++i){
threads1.create_thread(boost::bind(f,j));
sleep(1);}
threads1.join_all();
}

int main(int argc, char *argv[])
{
size_t nb_threads;
#if threadbool
nb_threads=boost::thread::hardware_concurrency();
#else
nb_threads=1;
#endif

slip::RegularVector3dField3d<double> volume1b;
read_slip_data(volume1b,"volume_lif_00005.abe");

slip::Volume<double> volume1(volume1b.dim1(),volume1b.dim2(),volume1b.dim3()),volume2;
slip::RegularVector3dField3d<double>::const_iterator iter1=volume1b.begin();
slip::Volume<double>::iterator iter2=volume1.begin();

for(	;
	iter1!=volume1b.end()&&
	iter2!=volume1.end();
	++iter1,++iter2)
	(*iter2)=(*iter1)[0];

vector<void*> args,tmp;
erode_class test(args);

int planstart=0,planstop=(int)volume1.dim1(),ordre=3;

// Creation of the vector containing parameters. I would like to avoid it ...
tmp.push_back(&volume1);
tmp.push_back(&volume2);
tmp.push_back(&ordre);
tmp.push_back(&planstart);
tmp.push_back(&planstop);

vector<bool> traites(volume1.dim1());
traitement f = &erode_class::thread_erode_b<slip::Volume<double> >;
GenericThreadedFunction_2Volumes(volume1,volume2,nb_threads,tmp,test,planstart,planstop,traites,f);


return 0;
}
I believe Boost::bind is what you're looking for. Then the generic function would look something like this (not an actual example of Boost::bind syntax):
1
2
3
4
5
6
template <typename T>
void generic_boost_bind_caller_function(T &bind){
    //...
    bind();
    //...
}
I finally found a way to do it!
I just put my function and its parameters in a class then I can lunch it in a thread easily.
So now i have one class for each treatment instead of a function :)

Thanks for your help!
See you


Here is my code for those who are interested :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#include "Mes_Fonctions.hpp"

#ifndef threadbool
	#define threadbool 0
	#ifdef THREAD
	#include <boost/thread/thread.hpp>
	#undef threadbool
	#define threadbool 1
	#endif
#endif

using namespace std;

#if threadbool
boost::mutex mutexTraitement;
#endif

int next_plane(vector<bool> &indices,int k){
for(int i=k+1;i<(int)indices.size();++i){
	if(!indices[i])return i;
	}
for(int i=k-1;i>0;--i){
	if(!indices[i])return i;
	}
return -10;
}



// ===================================================================
template<class T>
class erode_class{

public:

erode_class(T &vol1,T &vol2, int ord,int plan1):volume(vol1),volume2(vol2),ordre(ord),planstart(plan1){};

void treatment(vector<bool> &indices){
	int k=next_plane(indices,planstart);
	const int dim1=volume.dim1(),dim2=volume.dim2(),dim3=volume.dim3();
	
	// Traitements ...

	}

private:
	T &volume;
	T &volume2;
	int ordre;
	int planstart;

};

// =========================================================================
template <class T,class TT>
void GenericThreadedFunction_2Volumes(T &volume1,T &volume2,const size_t nb_threads,TT &Treatment_Type,int &planstart,vector<bool> &indices){
volume2.resize(volume1.dim1(),volume1.dim2(),volume1.dim3());
#if threadbool
boost::thread_group threads1;
size_t box_size=volume1.dim1();
for (std::size_t n=0; n<nb_threads; ++n){
	size_t nb_plans=(int)((double)box_size/(double)nb_threads);
	if(n<box_size%nb_threads)nb_plans++;
	if(n>=box_size%nb_threads){
		planstart=n*nb_plans+box_size%nb_threads;
		}
	else {
		planstart=n*nb_plans;
		}
	threads1.create_thread(boost::bind(&TT::treatment,boost::ref(Treatment_Type),boost::ref(indices)));
}
threads1.join_all();
#else
planstart=0;
Treatment_Type.treatment(indices);
#endif
}

int main(int argc, char *argv[])
{
size_t nb_threads;
#if threadbool
nb_threads=boost::thread::hardware_concurrency();
#else
nb_threads=1;
#endif

slip::RegularVector3dField3d<double> volume1b;
read_slip_data(volume1b,"volume_lif_00005.abe");

slip::Volume<double> volume1(volume1b.dim1(),volume1b.dim2(),volume1b.dim3()),volume2;
slip::RegularVector3dField3d<double>::const_iterator iter1=volume1b.begin();
slip::Volume<double>::iterator iter2=volume1.begin();
for(	;
	iter1!=volume1b.end()&&
	iter2!=volume1.end();
	++iter1,++iter2)
	(*iter2)=(*iter1)[0];

int planstart=0,ordre=3;

vector<bool> indices(volume1.dim1());
erode_class<slip::Volume<double> > erosion1(volume1,volume2,ordre,planstart);
GenericThreadedFunction_2Volumes(volume1,volume2,nb_threads,erosion1,planstart,indices);
return 0;

}
Topic archived. No new replies allowed.