I come from the python world where I could define a chain of operations and call them in a for loop:
class AddOne:
def __call__(self, x, **common_kwargs):
return x+1
class Stringify:
def __call__(self, x, **common_kwargs):
return str(x)
class WrapNicely:
def __call__(self, s, **common_kwargs):
return "result="+s
data = 42
for operation in [AddOne(), Stringify(), WrapNicely()]:
data = operation(data)
output = data
(Note: the goal is to have complex operations. Ideally, common kwargs could be given)
What would be the equivalent in C++ if the return type can be different after each call?
I'm not sure I could find anything close but I may have search with wrong keywords…
C++ is statically typed, so options here are limited:
Create a chain of functions that can be determined at compile time.
Create functions with parameter and return type being the same
Return a type that could "store multiple alternative types" such as std::variant
For the first alternative you could create a class template that executes functions via recursive calls, but it's a bit more complex than your python code:
template<class...Fs>
class Functions
{
std::tuple<Fs...> m_functions;
template<size_t index, class Arg>
decltype(auto) CallHelper(Arg&& arg)
{
if constexpr (index == 0)
{
return std::forward<Arg>(arg);
}
else
{
return std::get<index - 1>(m_functions)(CallHelper<index - 1>(std::forward<Arg>(arg)));
}
}
public:
Functions(Fs...functions)
: m_functions(functions...)
{
}
template<class Arg>
decltype(auto) operator()(Arg&& arg)
{
return CallHelper<sizeof...(Fs)>(std::forward<Arg>(arg));
}
};
int main() {
Functions f{
[](int x) { return x + 1; },
[](int x) { return std::to_string(x); },
[](std::string const& s) { return "result=" + s; }
};
std::cout << f(42) << '\n';
}
Note: This requires the use of a C++ standard of at least C++17.
TL;DR
Use composition from ranges:
using std::views::transform;
auto fgh = transform(h) | transform(g) | transform(f);
auto fgh_x = std::array{42} | fgh; // Calculate f(g(h(x)))
// single element range ^^
// ^^ ranges::single_view{42} is an alternative
std::cout << fgh_x[0]; // Result is the only element in the array.
Demo
DIY
I've written a series of articles on C++ functional programming years ago, for some thoughts on composition you can start from this one.
That said, you can also avoid the "functional nuances" and start from scratch. Here is a generic composer of callables:
template <class F, class... Fs>
auto composer(F&& arg, Fs&&... args)
{
return [fun = std::forward<F>(arg),
...functions = std::forward<Fs>(args)]<class X>(X&& x) mutable {
if constexpr (sizeof...(Fs))
{
return composer(std::forward<Fs>(functions)...)(
std::invoke(std::forward<F>(fun), std::forward<X>(x)));
}
else
{
return std::invoke(std::forward<F>(fun), std::forward<X>(x));
}
};
}
which you'd use as:
// Store the composed function or call it right away.
composer(lambda1, lambda2, lambda3)(42);
Demo
When teaching C++ to python developers, you've got to be careful in order to overcome the "C++ is so complicated" prejudice.
In this regard, you have two options:
If you want to chain operations, you can directly nest lambdas just as in python. It's only a different syntax, see my anser below.
However, if you use the chaining more often and want to apply the linear compose(f,g,h) syntax (which save you from typing a few char's), you should generate a composer yourself. The other answers follow this path, and for brevity I'd suggest the answer of #NikosAthanasiou.
So, here is the short version: Given some variable x and assuming it is a number (as you apply +1), you can directly chain the lambdas:
auto operation = [](auto x) { return [](auto y) { return "result="+std::to_string(y); }(x+1); };
ans use it as
std::vector<int> v; // -> fill the vector v
std::vector<std::string> w;
for(auto& x : v)
{
w.push_back(operation(x));
}
Only thing which you miss is the in-place mutation from int to string. For this, see the other answers using a std::variant, but why you should? ... use it only when you really need it.
Related
I want to have a map to save different types of value, so I have a map like
std::map<int, std::variant<int, std::string>> m ={{1,1},{2,"asd"}};
And Now I want to design a function to get the value by its key like
auto get(int key) {
...
return value;
}
That's to say what I want is like
get(1) -> 1
get(2) -> "asd"
So is it possible? If so what's the exact solution?
ADDED:
Yes, as for the purpose of the design, I want to save config datas reading from config files.
And after reading I need to make some type changing or data transform.
Such as in config files like
a=1
b=asd
And after reading it, I want to save it as
m["a"]=int(3) ---->refers a=1 and needs to plus 2 after reading its actual data 1
m["b"]=std::string("asdasd") ---->refers b=asd and needs to double it
So I think it will be easier if there is an interface function to get the exact value by the key
You cannot exactly design a function with this syntax. Just imagine this:
int key = 0;
if (rand() % 2) {
key = 1;
} else {
key = 2;
}
auto value = get(key);
Here's the riddle:
Please tell me the return type of get and the type of value. There can be only one answer and you can only answer with a single type that will remain unchanging no matter how much time I ask you this question.
If you can't answer, well, the compiler cannot either.
However, it doesn't mean you can't design something similar that will do what you need.
You could just return the variant. This might not be exactly what you're looking for, but it's worth noting since it doesn't change any of the input of the get function.
You can also just send the expected type:
get<int>(key)
The implementation would look like this:
template<typename T>
auto get(int key) -> T {
return std::get<int>(m[key]);
}
If you cannot know the expected type, then you could just send a visitor:
get(key, [](auto value) -> std::size_t {
if constexpr (std::same_as<int, decltype(value)>) {
// do stuff with value as an int since it's a int here
return value + 1;
} else {
// do stuff with value as a string since it's a string here
return value.size();
}
});
The implementation would look like this:
auto get(int key, auto visitor) -> decltype(auto) {
return std::visit(visitor, m[key]);
}
If I understand the requirements, you could return a proxy object from get that keeps a reference to the variant and depending on what you do with the proxy object, it can adapt. Example:
#include <iostream>
#include <variant>
#include <map>
#include <string>
struct Foo {
using variant = std::variant<int, std::string>;
// the proxy object
struct proxy {
// assignment:
template<class T>
variant& operator=(T&& value) {
*data = std::forward<T>(value);
return *data;
}
// for static_cast to the wanted type
explicit operator int& () { return std::get<int>(*data); }
explicit operator std::string& () { return std::get<std::string>(*data); }
variant* data;
};
proxy get(int key) {
// return the proxy object
return proxy{&m[key]};
}
std::map<int, std::variant<int, std::string>> m = {{1,1}, {2,"asd"}};
};
And it could be used like this:
int main() {
Foo f;
// you need to know what it stores:
std::cout << static_cast<int>(f.get(1)) << '\n';
std::cout << static_cast<std::string>(f.get(2)) << '\n';
// assigning is simple:
f.get(1) = "hello world"; // was int, now std::string
f.get(2) = 2; // was std::string, now int
std::cout << static_cast<std::string>(f.get(1)) << '\n';
std::cout << static_cast<int>(f.get(2)) << '\n';
}
Two possibilities:
A) You want to return either int or std::string depending on the index passed to the function.
Not possible. A function has one return type. Also auto is not magic. If you cannot write the actual type, then the compiler can't either.
B) You want to return the mapped_type of the map.
You can return std::variant<int, std::string> from the function.
Your get can't do much "better" than std::variant::get. With std::variant::get you need to specify at compile time what type you want to retrieve. And there is no way around that if you want to get either int or std::string.
There might be better ways to solve your actual issue, the one for which you thought auto get(int key) was the solution. A comment (by Eljay) mentions std::visit. There you can see a couple of examples of how to deal with variants.
I have a function that takes a T and calls specific functions on the supplied object. Until now it was used from compile-time objects, so all was great. Minimal example:
#include <iostream>
struct A {
void fun() const { std::cout << "A" << std::endl; }
};
struct B {
void fun() const { std::cout << "B" << std::endl; }
};
template<class T>
void use_function(const T& param) {
param.fun();
}
int main() {
use_function(A{}); // "A"
use_function(B{}); // "B"
return 0;
}
Now I'm trying to use that use_function() with objects that get created at runtime and having a hard time. I can't use std::variant or std::any since I need to supply the type as template parameter for their access functions - although all their variants fulfil the function interface. Example for a (failing) variant approach:
using var_type = std::variant<A, B>;
struct IdentityVisitor {
template<class T>
auto operator()(const T& alternative) const -> T {
return alternative;
}
};
int main() {
var_type var = A{};
// error C2338: visit() requires the result of all potential invocations to have the same type and value category (N4828 [variant.visit]/2).
use_function(std::visit(IdentityVisitor{}, var));
return 0;
}
What is possible is directly calling the function with an appropriate type like this:
if (rand() % 2 == 0)
use_function(A{});
else
use_function(B{});
just storing it in between is what I can't get working.
I understand on a technical level but having trouble coming up with an elegant solution. Is there one? I know that I could rewrite the objects with even a lightweight inheritance - but was trying to see if it's feasible to avoid it altogether, even if just as an exercise to avoid OOP in favor of templates and concepts. I feel like variants should be working with this, but apparently not.
std::visit([](auto const& x) { use_function(x); }, var);
If overload sets were objects, you could pass use_function to std::visit directly. Because they aren't, you need to wrap it in something that will be instantiated as a call to the right overload.
std::visit([](auto const& x) { use_function(x); }, var);
Basically, I want to find a template in a parameter pack that satisfies some runtime conditions. Intuitively, I just want to iterate over my instantiations of the parameter pack and find the first which satisfies a condition. My current simplified toy implementation to demonstrate what I mean:
Find the struct of X and Y which satisfies their test() first.
struct X {
bool test(int i) {
flag = i > 10;
return flag;
}
bool flag;
std::string value = "X satisfied first";
};
struct Y {
bool test(int i) {
flag = i > 11;
return flag;
}
bool flag;
std::string value = "Y satiesfied first";
};
This struct finds the first struct of X and Y to satisfy the condition. In this example it increases an integer up to a given limit until one of the structs reports that its test() was successful.
template <typename... Ts> struct FindFirst {
static std::string find_first(int limit) {
return find_first_satisfying(limit, Ts{}...);
}
static std::string find_first_satisfying(int limit, Ts... ts) {
int i = 0;
bool satisfied = false;
while (i < limit && !satisfied) {
satisfied = (ts.test(i) || ...);
i++;
}
return extract(ts...);
}
template <typename T, typename... OtherTs>
static std::string extract(T t, OtherTs... ts) {
if (t.flag) {
return t.value;
} else {
if constexpr (sizeof...(OtherTs) > 0) {
return extract(ts...);
} else {
return "Nobody satiesfied condition";
}
}
}
};
This implementation generates as many different extract() functions with different signatures as there are templates in the pack. They get "recursively" called and result in a deep call stack (depends on the position of the satisfying struct) and large bytecode.
Is there a method to construct a loop (at compile-time) which tests each instantiation of the parameter pack and stops appropriately?
Also, any other suggestions on how to simplify the whole construct?
I would wrote your code something like that:
template <typename ... Ts>
std::string find_first_satisfying(int limit, Ts... ts)
{
for (int i = 0; i != limit; ++i) {
std::string res;
bool found = false;
([&](){ if (ts.test(i)) { found = true; res = ts.value; } return found;}() || ...);
if (found) { return res; }
}
return "Nobody satisfied condition";
}
Demo
No. It is possible that in C++23 it won't be like this but currently there is no guarantee.
But is there problem really? The only issue I see is that the code is hard to write and understand. Large bytecode is of little significance and optimizer should be able to inline and optimize everything - only debug performance should suffer as a result (and compile time)... Unless you write the program in a manner that makes optimizer/compiler unable to inline it (by hiding bodies of functions).
P.S. can't you somehow write extract as an operator and use the ... instead of recursion? Though, I think it is a bad idea for various reasons. (I see that #Jarod42 wrote it via lambda in another answer, it looks good to me.)
Question:
How do you pass a lambda expression into a function. for example:
int GetCoolInt()
{
int a = 3;
return AddToInt( [a] (int b) {
a += b;
});
}
int AddToInt(int func(int output))
{
return func(3);
}
int output = GetCoolInt();
Context of my issue:
I have the following 2 methods:
FindPlayerStartByTag calls FindActor to use logic that will be used in other methods.
//template <typename T>
APlayerStart* FindPlayerStartByTag(FName tag)
{
return FindActor<APlayerStart>(tag, [tag](TObjectIterator<APlayerStart> actor) {
APlayerStart* playerStart;
if (actor->PlayerStartTag == tag)
{
playerStart = *actor;
}
return playerStart;
});
}
This method is the logic to find a specific object in an iterator with the help of the FindPlayerStartByTag logic
template <typename T>
T* FindActor(FName tag, T* func(TObjectIterator<T> actor))
{
T* returnValue;
if (!tag.IsNone())
{
// Search for actor
for (TObjectIterator<T> itr; itr; ++itr)
{
if (itr->IsA(T::StaticClass()))
{
returnValue = func(itr);
if (returnValue)
{
break;
}
}
}
}
return returnValue;
}
I currently have this error:
I'm confused as to what it means, considering a third method:
template <typename T>
T* FindActorByTag(FName tag)
{
return FindActor<T>(tag, [tag](TObjectIterator<AActor> actor)
{
T* foundActor;
for (FName currentActorTag : actor->Tags)
{
if (tag == currentActorTag)
{
foundActor = *actor;
}
}
return foundActor;
});
}
compiles just fine.
I can see that by adding template <typename T> gets rid of the error (see the //template <typename T>, but i don't need this as in the case of APlayerStart, i am already aware of what type the method needs to be.
Anyone help explain this?
Thanks!
Edit:
Here is a version of the method that i was using before i refactored into here:
APlayerStart* FindPlayerStartByTag(FName tag)
{
/*return FindActor<APlayerStart>(tag, [tag](TObjectIterator<APlayerStart> actor) {
APlayerStart* playerStart;
if (actor->PlayerStartTag == tag)
{
playerStart = *actor;
}
return playerStart;
});*/
APlayerStart* returnValue;
if (!tag.IsNone())
{
// Search for actor
for (TObjectIterator<APlayerStart> itr; itr; ++itr)
{
if (itr->IsA(APlayerStart::StaticClass()) && itr->PlayerStartTag == tag)
{
returnValue = *itr;
}
}
}
return returnValue;
}
but compiles.
Edit 2:
This is how i intend on using the methods:
PlayerStart = ActorHelper->FindPlayerStartByTag(PlayerStartTag);
ATreasureChestActor* treasureChestActor = ActorHelper->FindActorByTag<ATreasureChestActor>(TreasureActorTagName);
Edit 3:
The issue seems to be coming from the closure usage!
This is with the use of a closure variable:
and this is without:
Your post is still a mess, with 4 different versions of the same issue. I will focus on the first code snippet as it seems to be the closest one to a [MCVE] and I will clarify how to properly use lambdas and function objects.
int AddToInt(int func(int output))
This is a little bit misleading. I suggest changing it to the equivalent, but more used:
int AddToInt(int (*func)(int))
This means: declaring a function named AddToInt which:
accepts a parameter of type "pointer to function accepting an int and returning an int" and
returns an int.
As you can see, your function accept a classic C function pointer. It won't accept a function object of any type. To note here is that lambdas without capture can be converted to a function pointer.
For instance keeping the above declaration:
AddToInt([](int b) { return b + 1; }); // ok, non-capturing lambda conversion to function pointer
AddToInt([a](int b) { return a + b; }); // error cannot convert capturing lambda to function pointer
The reason is simple to understand. A non-capturing lambda can be equivalent to a free function, but a capturing lambda has a state (formed by the capture set), so it is "more" than a simple, classical free function.
As you can see, accepting function pointers is very much an archaic idiom because of these limitations (and don't even think of passing any kind of function object - e.g. a class with operator() defined).
For accepting any kind of callable object you generally have two options: the general template or the standard std::function object.
Template object
template <class Fn>
int AddToInt1(Fn func)
{
return func(3);
}
Now you can call AddToInt1 with any kind of callable. Depending of the type of the deduced Fn type you can have zero overhead with this method. A downside is that you can accept any type, including non-callable ones, or ones with incorrect parameter or return types. Concepts will alleviate most of these downsides.
AddToInt1([](int b) { return b + 1; }); // OK
AddToInt1([a](int b) { return a + b; }); // OK
You also might want to add perfect forwarding (omitted in the example for brevity).
std::function
The other route is to use std::function:
int AddToInt2(std::function<int(int)> func)
The disadvantage here is the heaviness of the std::function object. It uses type erasure and that adds a significant amount of performance penalty (which can be perfectly acceptable depending on your usage).
AddToInt2([](int b) { return b + 1; }); // OK
AddToInt2([a](int b) { return a + b; }); // OK
Now, once you get the gist of the above there are some more problems with your code you need to figure out:
[a] (int b) { a += b;};
First of all, are you aware that this lambda does not return anything? Furthermore it tries to modify the captured by value a which is illegal, as the lambda's operator() is const by default for good reason. If you want the lambda to modify the outer a captured variable, then you need to capture it by reference:
[&a] (int b) { a += b;};
And now you have to really really be careful to not end up with a dangling reference.
But I suspect you meant:
AddToInt([a] (int b) { return a + b;});
But that is just pure speculation on my part.
Here is a fully working example:
template <class Fn>
int AddToInt1(Fn func)
{
return func(3);
}
int AddToInt2(std::function<int (int)> func)
{
return func(3);
}
int GetCoolInt()
{
int a = 3;
return AddToInt1([a] (int b) { return a + b;}); // OK
//return AddToInt2([a] (int b) { return a + b;}); // OK
}
There are some important points I just mentioned here, but elaborating on them would be equivalent to writing a full tutorial on lambdas and beyond, which is out of the scope of this site. In conclusion you have to study the subject on your own.
Suppose I have a class:
class Widget {
public:
void initialize() {
// hurr-durr
};
int computeAnswer() {
return -42;
};
std::string getQuestion() {
return "The question";
};
};
It performs some computation, can do whatever it wants.
Now I want to augment it - apply an aspect, say one that logs each method call.
If I implemented this by hand, I'd implement all methods in this fashion:
int LoggingWidget::computeAnswer(){
log << 'Calling method computeAnswer';
int result = Widget::computerAnswer();
log << 'Result = ' << result;
return result;
}
I'd like the solution to be as generic as possible (I don't want to manually forward all calls), so the possible usages could include one of these (whichever is possible)
Widget* w = new LoggingWidget(); // either a class that inherits from Widget
// and automatically forwards all calls.
Widget* w = new Logging<Widget>(); // or a template that does this.
so that when I call
int result = w.computeAnswer();
The calls will be logged. Perhaps the new ellipsis operator (...) could come in handy here?
This isn't directly possible, since you can't inspect a class to see which members it has.
However, you can do something close:
Logging<Widget> w(widget);
w([&](Widget& w){
return w.computeAnswer();
});
Where Logging::operator() looks like follows:
/* somewhere in class: T wrapped; */
template<class F>
auto operator()(F&& f)
-> decltype(f(wrapped))
{
pre_log();
auto&& result = f(wrapped);
post_log(result);
return result;
}
It won't get better much better than this for totally generic code, since C++ has no (static) reflection.
Expanding on Xeo's answer, if you use decltype or result_of rather than auto && you also get copy elision.
template<typename F>
auto operator()(F &&f) -> decltype(std::forward<F>(f)(wrapped))
{
pre_log();
decltype(std::forward<F>(f)(wrapped)) result = std::forward<F>(f)(wrapped);
post_log(result);
return result;
}
In C++14 you can shorten this to:
template<typename F>
decltype(auto) operator()(F &&f)
{
pre_log();
decltype(auto) result = std::forward<F>(f)(wrapped);
post_log(result);
return result;
}