Reputation: 500
I'm a bit rusty in C++ and I'm switching after a year of python. Naturally I would like to translate the laziness coming from python to C++.
I just discovered rot13 and I'm all excited about it. I found 3 ways of doing it and I wanted to do a little performance test. I wanted to see also if there is a difference in modifying the string in place or creating a new one. So I ended up having 6 functions.
In the first method, I use a std::map to map the characters, thus I've built a class that initializes the map, in the second I use a ternary operator, and the third I use a bit shift.
Now the functions prototypes look like this
// map dependent
void Rot13::convert_inplace(string& mystr){
string Rot13::convert(const string& mystr){
// ternary operator
void bitrot_inplace(string& mystr){
string bitrot(const string& mystr){
// bit shift
void bitshiftrot_inplace(string& mystr){
string bitshiftrot(const string& mystr){
I wanted to construct a function that accept those functions as arguments to then calculate the time and print the results
So I had a look at stackoverflow, 1, 2, and I came up with this
typedef void (*vfc)(string str);
void printtime_inplace(string title, vfc func){
I tried this construction yet this means I'm limited by the vfc
return type which in my case is either void
or string
, and by the fact I need to pass the pointer of the class.
Thus I will have to do 3 functions to accommodate the different functions, namely a function for the class member function, a function for the void return type and a function for the string return type.
So I asked myself, is this the case where I really need to use templates to not write 3 times the same function? I'm really not confident with templates but should I do 3 typedefs
and structure the printtime function to accept a template? Moreover is there a way to tell the template you will accept only these types (namely the one I defined)?
An other question, is this let's say a good design? or would you suggest an other design? An other implementation?
Upvotes: 9
Views: 25812
Reputation: 3088
If you want to have a function with variable arguments, here is an example that builds on user0042s answer (see also https://github.com/goblinhack/c-plus-plus-examples/blob/master/std_function_with_variadic_template/README.md)
#include <iostream>
int my_wrapped_function (int x, const std::string y)
{
std::cout << "SUCCESS: Hello from my_wrapped_function(x=" << x << ", y=" << y << ");" << std::endl;
return 43;
}
void my_argument_modifier (int &x)
{
std::cout << "SUCCESS: Hello from my_argument_modifier(x=" << x << ") => " << x + 1 << ";" << std::endl;
x++;
}
template<typename ret, typename T, typename... Rest>
using fn = std::function<ret(T, Rest...)>;
template<typename ret, typename T, typename... Rest>
ret wrapper(fn<ret, T, Rest...> f, T t, Rest... rest)
{
return f(t, rest...);
}
template<typename ret, typename T, typename... Rest>
ret wrapper(fn<ret, T&, Rest&...> f, T& t, Rest&... rest)
{
return f(t, rest...);
}
int main()
{
// Wrap a function with variable arguments
auto f1 = fn<int,int,const std::string>(my_wrapped_function);
auto result = wrapper(f1, 42, std::string("hello"));
// Result should be 43:
// Wrap a function that modifies its arguments
auto f2 = fn<void,int&>(my_argument_modifier);
wrapper(f2, result);
// Result should be 44:
return 0;
}
Upvotes: 0
Reputation: 180415
The easiest way, IMO, is to use a template instead of trying to write a function with a concrete type.
template<typename Function>
void printtime_inplace(string title, Function func)
{
//...
func(title);
//...
}
This will now allow you to take anything that is a "function". You can pass it a regular function, a functor, a lambda, a std::function
, basically, any callable. The compiler will stamp out different instantiations for you but as far as your code is concerned you are calling the same function.
Upvotes: 17
Reputation: 8018
You can use std::function
to provide such template:
#include <iostream>
#include <functional>
#include <string>
#include <type_traits>
void convert_inplace(std::string& mystr){}
std::string convert(const std::string& mystr){
return mystr;
}
void bitrot_inplace(std::string& mystr){}
template<typename ret, typename par>
using fn = std::function<ret(par)>;
template<typename ret, typename par>
void caller(fn<ret,par> f) {
typename std::remove_reference<par>::type p;
ret r = f(p);
}
template<typename par>
void caller(fn<void,par> f) {
typename std::remove_reference<par>::type p;
f(p);
}
int main() {
auto f1 = fn<void,std::string&>(convert_inplace);
auto f2 = fn<std::string,const std::string&>(convert);
auto f3 = fn<void,std::string&>(bitrot_inplace);
caller(f1);
caller(f2);
caller(f3);
return 0;
}
See the live demo.
Upvotes: 9