1 /** \file
2  * \brief Definition of utility functions for FME layout.
3  *
4  * \author Martin Gronemann
5  *
6  * \par License:
7  * This file is part of the Open Graph Drawing Framework (OGDF).
8  *
9  * \par
10  * Copyright (C)<br>
11  * See README.md in the OGDF root directory for details.
12  *
13  * \par
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * Version 2 or 3 as published by the Free Software Foundation;
17  * see the file LICENSE.txt included in the packaging of this file
18  * for details.
19  *
20  * \par
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * \par
27  * You should have received a copy of the GNU General Public
28  * License along with this program; if not, see
29  * http://www.gnu.org/copyleft/gpl.html
30  */
31 
32 #pragma once
33 
34 #include <ogdf/basic/GraphAttributes.h>
35 
36 #ifdef OGDF_SYSTEM_UNIX
37 #include <sys/time.h>
38 #endif
39 
40 #ifdef OGDF_FME_KERNEL_USE_SSE
41 #include <ogdf/basic/internal/intrinsics.h>
42 #endif
43 
44 namespace ogdf {
45 namespace fast_multipole_embedder {
46 
47 // use SSE for Multipole computations
48 //#define OGDF_FME_KERNEL_USE_SSE
49 
50 // simple parallel quadtree sort
51 //#define OGDF_FME_PARALLEL_QUADTREE_SORT
52 
53 // use SSE for direct interaction (this is slower than the normal direct computation)
54 //#define OGDF_FME_KERNEL_USE_SSE_DIRECT
55 
OGDF_FME_Print_Config()56 inline void OGDF_FME_Print_Config()
57 {
58 #ifdef OGDF_FME_KERNEL_USE_SSE
59 	std::cout << "OGDF_FME_KERNEL_USE_SSE" << std::endl;
60 #endif
61 #ifdef OGDF_FME_PARALLEL_QUADTREE_SORT
62 	std::cout << "OGDF_FME_PARALLEL_QUADTREE_SORT" << std::endl;
63 #endif
64 #ifdef OGDF_FME_KERNEL_USE_SSE_DIRECT
65 	std::cout << "OGDF_FME_KERNEL_USE_SSE_DIRECT" << std::endl;
66 #endif
67 }
68 
69 using MortonNR = uint64_t;
70 using CoordInt = uint32_t;
71 
72 template<typename T>
is_align_16(T * ptr)73 inline bool is_align_16(T* ptr)
74 {
75 	return !(((size_t)(void*)ptr) & 0x0F);
76 }
77 
78 template<typename T>
align_16_prev_ptr(T * t)79 inline T* align_16_prev_ptr(T* t)
80 {
81 	return (T*)(((size_t)((void*)t))&~ 0x0F);
82 }
83 
84 template<typename T>
align_16_next_ptr(T * t)85 inline T* align_16_next_ptr(T* t)
86 {
87 	return (T*)((((size_t)((void*)t)) + 15)&~ 0x0F);
88 }
89 
90 #ifdef OGDF_SYSTEM_UNIX
GetDiffTime(timeval _then,double & dtime)91 inline timeval GetDiffTime(timeval _then, double& dtime)
92 {
93 	timeval then = (timeval) _then;
94 	timeval now;
95 	gettimeofday(&now, nullptr);
96 	timeval diff;
97 
98 	diff.tv_sec = now.tv_sec - then.tv_sec;
99 	diff.tv_usec = now.tv_usec - then.tv_usec;
100 	while(diff.tv_usec < 0)
101 	{
102 		diff.tv_sec--;
103 		diff.tv_usec = 1000000 + now.tv_usec - then.tv_usec;
104 	}
105 
106 	dtime = (double) diff.tv_sec;
107 	dtime += (double) diff.tv_usec / 1e6;
108 
109 	return (timeval) now;
110 }
111 #endif
112 
printProfiledTime(double t,const char * text)113 inline void printProfiledTime(double t, const char* text) { std::cout << t <<"s\t" << text << std::endl; }
printProfiledTime(double t,double sum,const char * text)114 inline void printProfiledTime(double t, double sum, const char* text) { std::cout << t <<"s\t" << text << "\t" << (t / sum)*100.0 <<"%"<< std::endl; }
115 
116 //! 16-byte aligned memory allocation macro
117 #define OGDF_MALLOC_16(s) System::alignedMemoryAlloc16((s))
118 
119 //! 16-byte aligned memory deallocation macro
120 #define OGDF_FREE_16(ptr) System::alignedMemoryFree((ptr))
121 
122 //! common template for bit-interleaving to compute the morton number  assumes sizeOf(MNR_T) = 2*sizeOf(C_T)
123 template<typename MNR_T, typename C_T>
mortonNumber(C_T ix,C_T iy)124 inline MNR_T mortonNumber(C_T ix, C_T iy)
125 {
126 	MNR_T x = (MNR_T)ix;
127 	MNR_T y = (MNR_T)iy;
128 	// bit length of the result
129 	const unsigned int BIT_LENGTH = static_cast<unsigned int>(sizeof(MNR_T)) << 3;
130 	// set all bits
131 	MNR_T mask = 0x0;
132 	mask = ~mask;
133 
134 	for (unsigned int i = (BIT_LENGTH >> 1);i>0; i = i >> 1) {
135 		// increase frequency
136 		mask = mask ^ (mask << i);
137 		x = (x | (x << i)) & mask;
138 		y = (y | (y << i)) & mask;
139 	}
140 	return x | (y << 1);
141 }
142 
143 
144 //! common template for extracting the coordinates from a morton number assumes sizeOf(MNR_T) = 2*sizeOf(C_T)
145 template<typename MNR_T, typename C_T>
mortonNumberInv(MNR_T mnr,C_T & x,C_T & y)146 inline void mortonNumberInv(MNR_T mnr, C_T& x, C_T& y)
147 {
148 	// bit length of the coordinates
149 	unsigned int BIT_LENGTH = static_cast<unsigned int>(sizeof(C_T)) << 3;
150 	// set least significant bit
151 	MNR_T mask = 0x1;
152 	// set coords to zero
153 	x = y = 0;
154 	for (unsigned int i=0; i < BIT_LENGTH; i++)
155 	{
156 		x = (C_T)(x | (mnr & mask));
157 		mnr = mnr >> 1;
158 		y = (C_T)(y | (mnr & mask));
159 		mask = mask << 1;
160 	}
161 }
162 
163 //! returns the index of the most signficant bit set. 0 = most signif, bitlength-1 = least signif
164 template<typename T>
mostSignificantBit(T n)165 inline uint32_t mostSignificantBit(T n)
166 {
167 	uint32_t BIT_LENGTH = static_cast<uint32_t>(sizeof(T)) << 3;
168 	T mask = 0x1;
169 	mask = mask << (BIT_LENGTH - 1);
170 	for (uint32_t i = 0; i < BIT_LENGTH; i++)
171 	{
172 		if (mask & n)
173 			return i;
174 		mask = mask >> 1;
175 	}
176 	return BIT_LENGTH;
177 }
178 
179 //! returns the prev power of two
prevPowerOfTwo(uint32_t n)180 inline uint32_t prevPowerOfTwo(uint32_t n)
181 {
182 	uint32_t msb = 32 - mostSignificantBit(n);
183 	return 0x1 << (msb - 1);
184 }
185 
186 //! utility class to select multiple nodes randomly
187 class RandomNodeSet
188 {
189 public:
190 	//! init the random node set with the given graph. takes O(n)
RandomNodeSet(const Graph & G)191 	explicit RandomNodeSet(const Graph& G) : m_graph(G) { allocate(); }
192 
193 	//! destructor
~RandomNodeSet()194 	~RandomNodeSet() { deallocate(); }
195 
196 	//! chooses a node from the available nodes in O(1)
chooseNode()197 	node chooseNode() const
198 	{
199 		int i = m_numNodesChoosen + ogdf::randomNumber(0,nodesLeft()-1);//(int)((double)nodesLeft()*rand()/(RAND_MAX+1.0));
200 		return m_array[i];
201 	}
202 
203 	//! removes a node from available nodes (assumes v is available) in O(1)
removeNode(node v)204 	void removeNode(node v)
205 	{
206 		int i = m_nodeIndex[v];
207 		int j = m_numNodesChoosen;
208 		node w = m_array[j];
209 		std::swap(m_array[i], m_array[j]);
210 		m_nodeIndex[w] = i;
211 		m_nodeIndex[v] = j;
212 		m_numNodesChoosen++;
213 	}
214 
isAvailable(node v)215 	bool isAvailable(node v) const { return m_nodeIndex[v] >= m_numNodesChoosen; }
216 
217 	//! number of nodes available
nodesLeft()218 	int nodesLeft() const { return m_numNodes - m_numNodesChoosen; }
219 
220 private:
allocate()221 	void allocate()
222 	{
223 		m_array = new node[m_graph.numberOfNodes()];
224 		m_nodeIndex.init(m_graph);
225 		m_numNodes = m_graph.numberOfNodes();
226 		m_numNodesChoosen = 0;
227 		int i = 0;
228 		for(node v : m_graph.nodes)
229 		{
230 			m_array[i] = v;
231 			m_nodeIndex[v] = i;
232 			i++;
233 		}
234 	}
235 
deallocate()236 	void deallocate()
237 	{
238 		delete[] m_array;
239 	}
240 
241 	//! the graph
242 	const Graph& m_graph;
243 
244 	//! the set of all nodes (at the end the available nodes)
245 	node* m_array;
246 
247 	//! the index in the array of the nodes
248 	NodeArray<int> m_nodeIndex;
249 
250 	//! total num nodes
251 	int m_numNodes;
252 
253 	//! num available nodes
254 	int m_numNodesChoosen;
255 };
256 
gridGraph(Graph & G,int n,int m)257 inline void gridGraph(Graph& G, int n, int m)
258 {
259 	G.clear();
260 	node v;
261 	node* topRow = new node[m];
262 	topRow[0] = G.newNode();
263 	for (int j=1; j<m; j++)
264 	{
265 		topRow[j] = G.newNode();
266 		G.newEdge(topRow[j-1], topRow[j]);
267 	}
268 	for (int i=1; i<n; i++)
269 	{
270 		v = G.newNode();
271 		G.newEdge(topRow[0], v);
272 		topRow[0] = v;
273 		for (int j=1; j<m; j++)
274 		{
275 			v = G.newNode();
276 			G.newEdge(topRow[j-1], v);
277 			G.newEdge(topRow[j], v);
278 			topRow[j] = v;
279 		}
280 	}
281 	delete[] topRow;
282 }
283 
284 inline void randomGridGraph(Graph& G, int n, int m, double missinNodesPercentage = 0.03)
285 {
286 	gridGraph(G, n, m);
287 	int numberOfNodesToDelete = (int)((double)G.numberOfNodes() * missinNodesPercentage);
288 
289 	RandomNodeSet rndSet(G);
290 	for(int i=0; i<numberOfNodesToDelete;i++)
291 	{
292 		node v = rndSet.chooseNode();
293 		rndSet.removeNode(v);
294 		G.delNode(v);
295 	}
296 }
297 
298 //! binomial coeffs from Hachuls FMMM
299 template<class TYP>
300 class BinCoeff
301 {
302 public:
BinCoeff(unsigned int n)303 	explicit BinCoeff(unsigned int n) : m_max_n(n) {	init_array(); }
304 
~BinCoeff()305 	~BinCoeff() { free_array(); }
306 
307 	//! Init BK -matrix for values n, k in 0 to t.
init_array()308 	void init_array()
309 	{
310 		using ptr = TYP*;
311 		unsigned int i,j;
312 		m_binCoeffs = new ptr[m_max_n+1];
313 		for(i = 0;i<= m_max_n ;i++)
314 		{
315 			m_binCoeffs[i]=  new TYP[i+1];
316 		}
317 
318 		//Pascalsches Dreieck
319 		for (i = 0; i <= m_max_n;i++)
320 		{
321 			m_binCoeffs[i][0] = m_binCoeffs[i][i] = 1;
322 		}
323 
324 		for (i = 2; i <= m_max_n; i ++)
325 		{
326 			for (j = 1; j < i; j++)
327 			{
328 				m_binCoeffs[i][j] = m_binCoeffs[i-1][j-1]+m_binCoeffs[i-1][j];
329 			}
330 		}
331 	}
332 
333 	//! Free space for BK.
free_array()334 	void free_array()
335 	{
336 		unsigned int i;
337 		for(i = 0;i<= m_max_n;i++)
338 		{
339 			delete[] m_binCoeffs[i];
340 		}
341 		delete[] m_binCoeffs;
342 	}
343 
344 	//Returns n over k.
value(unsigned int n,unsigned int k)345 	inline TYP value(unsigned int n, unsigned int k) const
346 	{
347 		return m_binCoeffs[n][k];
348 	}
349 
350 private:
351 	unsigned int m_max_n;
352 
353 	//! holds the binominal coefficients
354 	TYP** m_binCoeffs;
355 };
356 
357 
358 // nothing
359 struct EmptyArgType {};
360 
361 //
362 // Function Invoker for 8 args
363 //
364 template<typename FunctionType,
365          typename ArgType1 = EmptyArgType,
366          typename ArgType2 = EmptyArgType,
367          typename ArgType3 = EmptyArgType,
368          typename ArgType4 = EmptyArgType,
369          typename ArgType5 = EmptyArgType,
370          typename ArgType6 = EmptyArgType,
371          typename ArgType7 = EmptyArgType,
372          typename ArgType8 = EmptyArgType>
373 struct FuncInvoker
374 {
FuncInvokerFuncInvoker375 	FuncInvoker(FunctionType f, ArgType1 _arg1, ArgType2 _arg2, ArgType3 _arg3, ArgType4 _arg4, ArgType5 _arg5, ArgType6 _arg6, ArgType7 _arg7, ArgType8 _arg8) :
376 		function(f), arg1(_arg1), arg2(_arg2), arg3(_arg3), arg4(_arg4), arg5(_arg5), arg6(_arg6), arg7(_arg7), arg8(_arg8) { }
377 
operatorFuncInvoker378 	inline void operator()() { function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); }
379 
380 	FunctionType function;
381 	ArgType1 arg1;
382 	ArgType2 arg2;
383 	ArgType3 arg3;
384 	ArgType4 arg4;
385 	ArgType5 arg5;
386 	ArgType6 arg6;
387 	ArgType7 arg7;
388 	ArgType8 arg8;
389 };
390 
391 
392 //
393 // Function Invoker for 7 args
394 //
395 template<typename FunctionType, typename ArgType1, typename ArgType2, typename ArgType3, typename ArgType4, typename ArgType5, typename ArgType6, typename ArgType7>
396 struct FuncInvoker<FunctionType, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, ArgType7, EmptyArgType>
397 {
398 	FuncInvoker(FunctionType f, ArgType1 _arg1, ArgType2 _arg2, ArgType3 _arg3, ArgType4 _arg4, ArgType5 _arg5, ArgType6 _arg6, ArgType7 _arg7) :
399 		function(f), arg1(_arg1), arg2(_arg2), arg3(_arg3), arg4(_arg4), arg5(_arg5), arg6(_arg6), arg7(_arg7) { }
400 
401 	inline void operator()() { function(arg1, arg2, arg3, arg4, arg5, arg6, arg7); }
402 
403 	FunctionType function;
404 	ArgType1 arg1;
405 	ArgType2 arg2;
406 	ArgType3 arg3;
407 	ArgType4 arg4;
408 	ArgType5 arg5;
409 	ArgType6 arg6;
410 	ArgType7 arg7;
411 };
412 
413 //
414 // Function Invoker for 6 args
415 //
416 template<typename FunctionType, typename ArgType1, typename ArgType2, typename ArgType3, typename ArgType4, typename ArgType5, typename ArgType6>
417 struct FuncInvoker<FunctionType, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, EmptyArgType, EmptyArgType>
418 {
419 	FuncInvoker(FunctionType f, ArgType1 _arg1, ArgType2 _arg2, ArgType3 _arg3, ArgType4 _arg4, ArgType5 _arg5, ArgType6 _arg6) :
420 		function(f), arg1(_arg1), arg2(_arg2), arg3(_arg3), arg4(_arg4), arg5(_arg5), arg6(_arg6) { }
421 
422 	inline void operator()() { function(arg1, arg2, arg3, arg4, arg5, arg6); }
423 
424 	FunctionType function;
425 	ArgType1 arg1;
426 	ArgType2 arg2;
427 	ArgType3 arg3;
428 	ArgType4 arg4;
429 	ArgType5 arg5;
430 	ArgType6 arg6;
431 };
432 
433 //
434 // Function Invoker for 5 args
435 //
436 template<typename FunctionType, typename ArgType1, typename ArgType2, typename ArgType3, typename ArgType4, typename ArgType5>
437 struct FuncInvoker<FunctionType, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, EmptyArgType, EmptyArgType, EmptyArgType>
438 {
439 	FuncInvoker(FunctionType f, ArgType1 _arg1, ArgType2 _arg2, ArgType3 _arg3, ArgType4 _arg4, ArgType5 _arg5) :
440 		function(f), arg1(_arg1), arg2(_arg2), arg3(_arg3), arg4(_arg4), arg5(_arg5) { }
441 
442 	inline void operator()() { function(arg1, arg2, arg3, arg4, arg5); }
443 
444 	FunctionType function;
445 	ArgType1 arg1;
446 	ArgType2 arg2;
447 	ArgType3 arg3;
448 	ArgType4 arg4;
449 	ArgType5 arg5;
450 };
451 
452 //
453 // Function Invoker for 4 args
454 //
455 template<typename FunctionType, typename ArgType1, typename ArgType2, typename ArgType3, typename ArgType4>
456 struct FuncInvoker<FunctionType, ArgType1, ArgType2, ArgType3, ArgType4, EmptyArgType, EmptyArgType, EmptyArgType, EmptyArgType>
457 {
458 	FuncInvoker(FunctionType f, ArgType1 _arg1, ArgType2 _arg2, ArgType3 _arg3, ArgType4 _arg4) :
459 		function(f), arg1(_arg1), arg2(_arg2), arg3(_arg3), arg4(_arg4) { }
460 
461 	inline void operator()() { function(arg1, arg2, arg3, arg4); }
462 
463 	FunctionType function;
464 	ArgType1 arg1;
465 	ArgType2 arg2;
466 	ArgType3 arg3;
467 	ArgType4 arg4;
468 };
469 
470 //
471 // Function Invoker for 3 args
472 //
473 template<typename FunctionType, typename ArgType1, typename ArgType2, typename ArgType3>
474 struct FuncInvoker<FunctionType, ArgType1, ArgType2, ArgType3, EmptyArgType, EmptyArgType, EmptyArgType, EmptyArgType, EmptyArgType>
475 {
476 	FuncInvoker(FunctionType f, ArgType1 _arg1, ArgType2 _arg2, ArgType3 _arg3) :
477 		function(f), arg1(_arg1), arg2(_arg2), arg3(_arg3) { }
478 
479 	inline void operator()() { function(arg1, arg2, arg3); }
480 
481 	FunctionType function;
482 	ArgType1 arg1;
483 	ArgType2 arg2;
484 	ArgType3 arg3;
485 };
486 
487 //
488 // Function Invoker for 2 args
489 //
490 template<typename FunctionType, typename ArgType1, typename ArgType2>
491 struct FuncInvoker<FunctionType, ArgType1, ArgType2, EmptyArgType, EmptyArgType, EmptyArgType, EmptyArgType, EmptyArgType, EmptyArgType>
492 {
493 	FuncInvoker(FunctionType f, ArgType1 _arg1, ArgType2 _arg2) :
494 		function(f), arg1(_arg1), arg2(_arg2) { }
495 
496 	inline void operator()() { function(arg1, arg2); }
497 
498 	FunctionType function;
499 	ArgType1 arg1;
500 	ArgType2 arg2;
501 };
502 
503 //
504 // Function Invoker for 1 args
505 //
506 template<typename FunctionType, typename ArgType1>
507 struct FuncInvoker<FunctionType, ArgType1, EmptyArgType, EmptyArgType, EmptyArgType, EmptyArgType, EmptyArgType, EmptyArgType, EmptyArgType>
508 {
509 	FuncInvoker(FunctionType f, ArgType1 _arg1) :
510 		function(f), arg1(_arg1) { }
511 
512 	inline void operator()() { function(arg1); }
513 
514 	FunctionType function;
515 	ArgType1 arg1;
516 };
517 
518 //
519 // Function Invoker for 0 args
520 //
521 template<typename FunctionType>
522 struct FuncInvoker<FunctionType, EmptyArgType, EmptyArgType, EmptyArgType, EmptyArgType, EmptyArgType, EmptyArgType, EmptyArgType, EmptyArgType>
523 {
524 	FuncInvoker(FunctionType f) :
525 		function(f) { }
526 
527 	inline void operator()() { function(); }
528 
529 	FunctionType function;
530 };
531 
532 
533 template<typename FunctionType, typename ArgType1, typename ArgType2, typename ArgType3, typename ArgType4, typename ArgType5, typename ArgType6, typename ArgType7, typename ArgType8>
534 FuncInvoker<FunctionType, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, ArgType7, ArgType8>
535 createFuncInvoker(FunctionType func, ArgType1 _arg1, ArgType2 _arg2, ArgType3 _arg3, ArgType4 _arg4, ArgType5 _arg5, ArgType6 _arg6, ArgType7 _arg7, ArgType8 _arg8)
536 {
537 	return FuncInvoker<FunctionType, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, ArgType7, ArgType8>(func, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6, _arg7, _arg8);
538 }
539 
540 template<typename FunctionType, typename ArgType1, typename ArgType2, typename ArgType3, typename ArgType4, typename ArgType5, typename ArgType6, typename ArgType7, typename ArgType8>
541 FuncInvoker<FunctionType, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, ArgType7, ArgType8>
542 createFuncInvoker(FunctionType func, ArgType1 _arg1, ArgType2 _arg2, ArgType3 _arg3, ArgType4 _arg4, ArgType5 _arg5, ArgType6 _arg6, ArgType7 _arg7)
543 {
544 	return FuncInvoker<FunctionType, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, ArgType7, EmptyArgType>(func, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6, _arg7);
545 }
546 
547 template<typename FunctionType, typename ArgType1, typename ArgType2, typename ArgType3, typename ArgType4, typename ArgType5, typename ArgType6>
548 FuncInvoker<FunctionType, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6>
549 createFuncInvoker(FunctionType func, ArgType1 _arg1, ArgType2 _arg2, ArgType3 _arg3, ArgType4 _arg4, ArgType5 _arg5, ArgType6 _arg6)
550 {
551 	return FuncInvoker<FunctionType, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6>(func, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6);
552 }
553 
554 template<typename FunctionType, typename ArgType1, typename ArgType2, typename ArgType3, typename ArgType4, typename ArgType5>
555 FuncInvoker<FunctionType, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5>
556 createFuncInvoker(FunctionType func, ArgType1 _arg1, ArgType2 _arg2, ArgType3 _arg3, ArgType4 _arg4, ArgType5 _arg5)
557 {
558 	return FuncInvoker<FunctionType, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5>(func, _arg1, _arg2, _arg3, _arg4, _arg5);
559 }
560 
561 template<typename FunctionType, typename ArgType1, typename ArgType2, typename ArgType3, typename ArgType4>
562 FuncInvoker<FunctionType, ArgType1, ArgType2, ArgType3, ArgType4>
563 createFuncInvoker(FunctionType func, ArgType1 _arg1, ArgType2 _arg2, ArgType3 _arg3, ArgType4 _arg4)
564 {
565 	return FuncInvoker<FunctionType, ArgType1, ArgType2, ArgType3, ArgType4>(func, _arg1, _arg2, _arg3, _arg4);
566 }
567 
568 template<typename FunctionType, typename ArgType1, typename ArgType2, typename ArgType3>
569 FuncInvoker<FunctionType, ArgType1, ArgType2, ArgType3>
570 createFuncInvoker(FunctionType func, ArgType1 _arg1, ArgType2 _arg2, ArgType3 _arg3)
571 {
572 	return FuncInvoker<FunctionType, ArgType1, ArgType2, ArgType3>(func, _arg1, _arg2, _arg3);
573 }
574 
575 template<typename FunctionType, typename ArgType1, typename ArgType2>
576 FuncInvoker<FunctionType, ArgType1, ArgType2>
577 createFuncInvoker(FunctionType func, ArgType1 _arg1, ArgType2 _arg2)
578 {
579 	return FuncInvoker<FunctionType, ArgType1, ArgType2>(func, _arg1, _arg2);
580 }
581 
582 template<typename FunctionType, typename ArgType1>
583 FuncInvoker<FunctionType, ArgType1>
584 createFuncInvoker(FunctionType func, ArgType1 _arg1)
585 {
586 	return FuncInvoker<FunctionType, ArgType1>(func, _arg1);
587 }
588 
589 template<typename FunctionType>
590 FuncInvoker<FunctionType>
591 createFuncInvoker(FunctionType func)
592 {
593 	return FuncInvoker<FunctionType>(func);
594 }
595 
596 }
597 }
598