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