creating a function with function as a argument

I have to create a program with sorting algorithms. The program will have to do all the algorithms in one go. I was thinking of using this code for all of algorithms multiple times.
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
for (int x = 0; x < l_wielkosci; x++)
	{
		mnoznik = pow(2, x);
		int * tab = new int[mnoznik*N];
		for (int i = 0; i < mnoznik*N; i++)
		{
			tab[i] = i;
		}

		cout << "Sortowanie babelkowe z " << N*mnoznik << " elementow.\n";
		for (int i = 0; i < l_testow; i++)
		{
			random_shuffle(tab, tab + mnoznik*N);
			start = clock();
			sort_babl(tab, mnoznik*N); //function with algorithm in it
			stop = clock();
			float czas = (float)(stop - start) / CLOCKS_PER_SEC;
			suma += czas;
			weryfikacja(tab, N, mnoznik);
			cout << czas << endl;

		}
		float srednia = suma / l_testow;
		cout << "srednia:" << srednia << endl << endl;
		ofstream plik("plik_z_danymi.txt", ios_base::app);
		plik << "Sortowanie babelkowe z " << N*mnoznik << " elementow.\n" << srednia << endl;
		plik.close();
		suma = 0.0;
		if (srednia > 5)
			break;
		delete[] tab;
	}


I am wondering how to make this code in one function so i could only change what algorithm is being called for in as an argument.
Make the type of the sort function a template type parameter:
1
2
3
4
5
6
7
8
template <typename Sort>
// In this context, Sort&& means "a forwarding reference to an object with type Sort",
// which will be deduced.
void sort_using(Sort&& sort_fn) { 
  // ... your set-up code
  // to call the sorting function:
  std::forward<Sort>(sort_fn)(tab, mnoznik*N);
}
Last edited on
Max – suppose it's a choice b/w std::sort() and std::partial_sort() i.e. 2 sorting functions but with different signatures. Could you please show how std::forward() would deal with these two functions in that case? Many thanks
You can factor sort_using into something minimal like it is below, and give it some adapter function, while std::forward doesn't change at all:
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
# include <iostream>
# include <vector>
# include <algorithm>

// note the equivalence to std::invoke!
template <typename Sort, typename RandomIt>
void sort_using(RandomIt&& begin, RandomIt&& end, Sort&& sort_fn) { 
    std::forward<Sort>(sort_fn)(std::forward<RandomIt>(begin), 
                                std::forward<RandomIt>(end));
}

int main() {
    auto sort_fn = std::sort<typename std::vector<int>::iterator>; 
    auto sort_first_three = [] (auto&& begin, auto&& end) {
        return std::partial_sort(std::forward<decltype(begin)>(begin), 
                                 std::forward<decltype(begin)>(begin) + 3, 
                                 std::forward<decltype(end)>(end)); };
                                       
    { // sort fully, do setup here
        std::vector<int> v {1, 2, 0, 6, 4, 7, 3, 5, 8, 9};
        sort_using(v.begin(), v.end(), sort_fn);
        for (int const& i: v)
            std::cout << i << ' ';
        std::cout << '\n';
    }
    
    { // sort partially, do setup here
        std::vector<int> v {1, 2, 0, 6, 4, 7, 3, 5, 8, 9};
        sort_using(v.begin(), v.end(), sort_first_three);
        for (int const& i: v)
            std::cout << i << ' ';
        std::cout << '\n';
    }
}

http://coliru.stacked-crooked.com/
http://coliru.stacked-crooked.com/a/799879f608590df9

But maybe I didn't answer your question. If you want to make the decision about how to call the function earlier (i.e., at compile-time), you could use function overload selection or SFINAE to choose. You can of course apply a little meta-programming to get whatever information you want about the function's signature. Is that what you're looking for?
Last edited on
mbozzi – thank you, as ever your reply is thoughtful, informative and certainly answers my query. You've also alluded to the possibility of calling the function earlier (i.e. at compile time) through some techniques that intrigues me and gives me something to ponder and research. Many thanks once again
I'd like not to repeat the section of code I posted earlier (like I do in code below). I have never used templates so they are unclear to me.

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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
// ConsoleApplication1.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <algorithm>
#include <time.h>
#include <math.h>
#include <fstream>

using namespace std;

void weryfikacja(int * tab, int n, int mnoznik);
void sort_babl(int tab[], int N);
void proste_wybieranie(int tab[], int N);
void proste_wstawianie(int tab[], int n);
void szybkie(int tab[], int d, int g);
void shell(int tab[], int n);
void sortowanie_przez_kopcowanie(int tab[], int n);
void kopiec_buduj(int tab[], int n);
void kopiec_w_dol(int tab[], int i, int n);


int main()
{
	int N = 1000;
	clock_t start;
	clock_t stop;
	float suma = 0.0;
	int mnoznik;
	int l_wielkosci = 3;
	int l_testow = 6;

	for (int x = 0; x < l_wielkosci; x++)
	{
		mnoznik = pow(2, x);
		int * tab = new int[mnoznik*N];
		for (int i = 0; i < mnoznik*N; i++)
		{
			tab[i] = i;
		}

		cout << "Sortowanie babelkowe z " << N*mnoznik << " elementow.\n";
		for (int i = 0; i < l_testow; i++)
		{
			random_shuffle(tab, tab + mnoznik*N);
			start = clock();
			sort_babl(tab, mnoznik*N);
			stop = clock();
			float czas = (float)(stop - start) / CLOCKS_PER_SEC;
			suma += czas;
			weryfikacja(tab, N, mnoznik);
			cout << czas << endl;

		}
		float srednia = suma / l_testow;
		cout << "srednia:" << srednia << endl << endl;
		ofstream plik("plik_z_danymi.txt", ios_base::app);
		plik << "Sortowanie babelkowe z " << N*mnoznik << " elementow.\n" << srednia << endl;
		plik.close();
		suma = 0.0;
		if (srednia > 5)
			break;
		delete[] tab;
	}
	for (int x = 0; x < l_wielkosci; x++)
	{
		mnoznik = pow(2, x);
		int * tab = new int[mnoznik*N];
		for (int i = 0; i < mnoznik*N; i++)
		{
			tab[i] = i;
		}

		cout << "Sortowanie przez wybieranie " << N*mnoznik << " elementow.\n";
		for (int i = 0; i < l_testow; i++)
		{
			random_shuffle(tab, tab + mnoznik*N);
			start = clock();
			proste_wybieranie(tab, mnoznik*N);
			stop = clock();
			float czas = (float)(stop - start) / CLOCKS_PER_SEC;
			suma += czas;
			weryfikacja(tab, N, mnoznik);
			cout << czas << endl;

		}
		float srednia = suma / l_testow;
		cout << "srednia:" << srednia << endl << endl;
		ofstream plik("plik_z_danymi.txt", ios_base::app);
		plik << "Sortowanie przez wybieranie " << N*mnoznik << " elementow.\n" << srednia << endl;
		plik.close();
		suma = 0.0;
		if (srednia > 5)
			break;
		delete[] tab;
	}

	return 0;
}


void sort_babl(int tab[], int N)
{

	for (int i = 0; i < N; i++)
	{
		for (int j = 1; j < N; j++)
		{
			if (tab[j - 1] > tab[j])
				swap(tab[j - 1], tab[j]);
		}
	}
}

void proste_wybieranie(int tab[], int n)
{
	for (int i = 0; i <= n; i++)
	{
		int k = i;
		int x = tab[i];
		for (int j = i + 1; j < n; j++)
		{
			if (tab[j] < x)
			{
				k = j;
				x = tab[j];
			}
		}
		tab[k] = tab[i];
		tab[i] = x;
	}
}

void proste_wstawianie(int tab[], int n)
{
	for (int i = 1; i < n; i++)
	{
		int x = tab[i];
		int j = i - 1;
		while ((j >= 0) && (tab[j]>x))
		{
			tab[j + 1] = tab[j];
			j = j - 1;
		}
		tab[j + 1] = x;
	}
}

void szybkie(int tab[], int d, int g)
{
	int s, t = 0;
	if (d < g)
	{
		t = tab[d];
		s = d;
		for (int i = d + 1; i < g; i++)
		{
			if (tab[i] < t)
			{
				s = s + 1;
				swap(tab[s], tab[i]);
			}
		}
		swap(tab[d], tab[s]);
		szybkie(tab, d, s);
		szybkie(tab, s + 1, g);
	}
}

void shell(int tab[], int n)
{
	int h = 1;
	int j, x;
	while (h < n / 9)
	{
		h = 3 * h + 1;
	}
	while (h > 0)
	{
		for (int i = h; i < n; i++)
		{
			x = tab[i];
			j = i;
			while (j >= h && x < tab[j - h])
			{
				tab[j] = tab[j - h];
				j = j - h;
			}
			tab[j] = x;
		}
		h = h / 3;
	}
}

void sortowanie_przez_kopcowanie(int tab[], int n)
{
	kopiec_buduj(tab, n);
	for (int i = n - 1; i > 1; i--)
	{
		swap(tab[0], tab[i]);
		n = n - 1;
		kopiec_w_dol(tab, 0, n);
	}
}
void kopiec_buduj(int tab[], int n)
{
	for (int i = (n - 1) / 2; i > 1; i--)
		kopiec_w_dol(tab, i, n);
}

void kopiec_w_dol(int tab[], int i, int n)
{
	int largest;
	int l = 2 * i + 1;
	int r = 2 * i + 2;

	if (l < n && tab[l] > tab[i])
		largest = l;
	else
		largest = i;

	if (r < n && tab[r] > tab[largest])
		largest = r;

	if (largest != i)
	{
		swap(tab[i], tab[largest]);
		kopiec_w_dol(tab, largest, n);
	}
}

void weryfikacja(int * tab, int n, int mnoznik)
{
	int g = mnoznik*n;
	for (int i = 0; i < g; ++i)
	{
		if (tab[i] != i)
		{
			cout << "Tablica nieposortowana!" << std::endl;
			delete[] tab;
			std::cin.get();
			exit(9);
		}
	}
}
When you're given two duplicate sections of code which compute the same thing but using different data:
1
2
3
4
5
6
7
8
9
10
int main() { 
  {  // section 1
    int value = 2;
    std::cout << (2 + value) << '\n';
  }
  {  // section 2
    int value = 4;
    std::cout << (2 + value) << '\n';
  }
}

It makes sense to create a function, and pass in the data as a parameter.
1
2
3
4
5
int add_two(int value) { return 2 + value; }
int main() {
  std::cout << add_two(2) << '\n';
  std::cout << add_two(4) << '\n';
}


When you have two duplicate sections of code in which do fundamentally the same thing (e.g., sorting a random collection) but in different ways (different sorting algorithms), it makes sense to create a function, and pass in the behavior which changes as a parameter. Behaviors are either functions or objects which appear like functions.

Here is one way to do this without templates:
1
2
3
4
5
6
7
8
// The type of Sort is a pointer to function returning nothing and accepting 
// a pointer to int and an int.
using Sort = void(*)(int*, int);
void sort_using(Sort sort_fn) {
  ...
  sort_fn(tab, mnoznik*N);
  ...
}

Complete example:
http://coliru.stacked-crooked.com/a/eabd9b758a165c2b

The above approach disallows the use of most objects which can be called like functions (because their types differ). This doesn't appear to be necessary for your program, but in the interest of generality we can make sort_using a function template.
Complete example:
http://coliru.stacked-crooked.com/a/3cd0d14a99d2d64b

If you are curious about how the template works, take a look at the tutorial on this site:
http://www.cplusplus.com/doc/oldtutorial/templates/

Also important for complete generality is perfect forwarding (see line 55 of the second example), although this topic is more complex and not as important.
http://thbecker.net/articles/rvalue_references/section_01.html
That's great. If a function takes 3 arguments do I make another function but with 3 arguments? Like that: void sort_using(void(*sort_fn)(int*, int, int))?


I've got one more problem with this code. The heapsort (sortowanie_przez_kopcowanie, kopiec_buduj and kopiec_w_dol functions) doesn't work. Do you have any idea why that'd be?
Topic archived. No new replies allowed.