Suppose I have the following:
x=Symbol('x')
f=Function('f')(x)
type(f)
f
Why is type(f)=f?
Why it is not UndefinedFunction?
How can I identify this kind of functions more generally?
Short answer
It's an instance of AppliedUndef, because it's been "applied" (to variable x). Before that, it was an UndefinedFunction.
Explanation
Let's consider two steps separately: creating a function and applying it. When they are mixed together, we lose the difference between functions and expressions, which is a common source of confusion in SymPy and in math generally.
Unapplied functions
>>> f = Function('f')
>>> type(f)
<class 'sympy.core.function.UndefinedFunction'>
This is an undefined function. Notably, it's a class and not an object. Perhaps it should be an object (a long-standing issue) but on the other hand, this behavior is consistent with defined functions (sin, log) being classes. You can test for f being an UndefinedFunction:
>>> from sympy.core.function import UndefinedFunction
>>> isinstance(f, UndefinedFunction)
True
Applied functions
When you apply f to x, creating f(x), the result is a SymPy expression of class f, just as sin(x) is an expression of class sin. How can we test this expression for being an undefined function applied to something? let's see:
>>> type(f(x)).__mro__
(f, AppliedUndef, Function, Application, <class 'sympy.core.expr.Expr'>, <class 'sympy.core.basic.Basic'>, <class 'sympy.core.evalf.EvalfMixin'>, <class 'object'>)
So, AppliedUndef is the class you want to check here.
>>> from sympy.core.function import AppliedUndef
>>> isinstance(f(x), AppliedUndef)
True
Conclusion
Test with isinstance(..., ...), not with type(...) == ...
Related
While trying to implement something similar to std::invoke, I noticed the following subtlety in the standard. I'm curious if anybody can give me a language lawyer explanation that helps decide whether libc++'s implementation is correct.
In N4868 (basically C++20), [func.invoke] defines std::invoke this way:
template<class F, class... Args>
constexpr invoke_result_t<F, Args...> invoke(F&& f, Args&&... args)
noexcept(is_nothrow_invocable_v<F, Args...>);
Returns: INVOKE(std::forward<F>(f), std::forward<Args>(args)...).
In turn, [func.require]/1 defines INVOKE as follows:
Define INVOKE(f, t_1, t_2, ..., t_N) as follows:
[...] when f is a pointer to a member function of a class T and [...]
[...] when f is a pointer to a member function of a class T and [...]
[...] when f is a pointer to a member function of a class T and [...]
[...] when N == 1 and f is a pointer to a data member of a class T and [...]
[...] when N == 1 and f is a pointer to a data member of a class T and [...]
f(t_1, t_2, ..., t_N) in all other cases.
My question is what should happen with the following snippet:
struct Callable {
int DoSomething() { return 17; }
};
int (Callable::*member_fn_ptr)() = &Callable::DoSomething;
assert(17 == std::invoke(member_fn_ptr, Callable{}));
This works under libc++, but in my implementation it causes a compilation error. That's because my implementation falls through to the "in all other cases" clause above, attempting to call the member function pointer as if it were a plain function pointer.
I thought this was a bug in my implementation until I read the standard a little more closely. The definition of std::invoke uses a forwarding reference for f, and clearly says it should be forwarded. In this case that means that it will be forwarded as an lvalue reference. Then in the cascade of logic in the definition of INVOKE, it seems like this should match the final case: f is not a pointer to a member function, but rather a reference to a pointer to a member function. And libc++'s implementation of <type_traits> agrees with me:
static_assert(!std::is_member_function_pointer_v<decltype(member_fn_ptr)&>)
In libc++ this works out only because the SFINAE for the first case of INVOKE checks whether std::decay_t<decltype(f)> is a member function pointer, not decltype(f).
Is this a bug in libc++, a bug in the standard, or a bug in my understanding of the standard?
As per the document, which says:
A Callable type is a type for which the INVOKE operation (used by,
e.g., std::function, std::bind, and std::thread::thread) is
applicable. This operation may be performed explicitly using the
library function std::invoke. (since C++17)
Requirements
The type T
satisfies Callable if Given f,
an object of type T ArgTypes,
suitable list of argument types R,
expressions must be valid:
std::declval()...) the expression is well-formed in
unevaluated contextExpression Requirements INVOKE(f,suitable return type The following
Question 1:
If I understand correctly, given void foo(int) {}, std::function(foo) is not callable, whereas std::function(foo, 1) is callable. Am I right?
Question 2:
What confuses me is the statement below.
As per the document, which says[emphasis mine]:
The class template std::packaged_task wraps any Callable target
(function, lambda expression, bind expression, or another function
object) so that it can be invoked asynchronously. Its return value or
exception thrown is stored in a shared state which can be accessed
through std::future objects.
As you see that the class template std::packaged_task wraps any Callable target, and std::function{foo} is not callable, but std::packged_task<void(int)> task{f}; compiles.
If one wants to be a successful professional, they do not stop reading the article on the introduction. If they continue reading the article and the referenced articles, they find all answers there.
Your first question is answered directly on that page:
otherwise, INVOKE(f, t1, t2, ..., tN) is equivalent to f(t1, t2, ..., tN) (that is, f is a FunctionObject).
Opening the reference:
A FunctionObject type is the type of an object that can be used on the left of the function call operator.
Therefore, void foo(int) {} is callable. The both std::function(foo) and std::function(foo, 1) will not compile. I guess you mean std::function f(foo), or std::function f{foo}, or std::function<void(int)> f(foo), the both are obviously callable, and I guess you confused std::bind(foo, 1) with std::function(foo, 1), that is also callable.
You know now, why std::packged_task<void(int)> task{f}; is compiled - because f is callable as it is figured out above.
I want to define a concept that would accept all callable objects.
Here's what I have done so far:
template<typename F>
concept Func = std::is_function_v<std::remove_pointer_t<std::decay_t<F>>> || (requires (F f) {
std::is_function_v<decltype(f.operator())>;
});
bool is_callable(Func auto&&) {
return true;
}
bool is_callable(auto&&) {
return false;
}
Yet if I define those:
auto f = [](auto a, auto b, auto c, auto d, auto e) {
return a * b * c * d * e;
};
int g(int a, int b) {
return a + b;
}
is_callable(g) is true but is_callable(f) is false, it did not work (I want both to return true).
So I tried to see if the following would compile:
decltype(f.operator()) // Reference to non-static member function must be called
decltype(&f.operator()) // Cannot create a non-constant pointer to member function
decltype(f::operator()) // 'f' is not a class, namespace, or enumeration
decltype(&f::operator()) // same as previously
It gave me the errors you can see as comment on those 4 lines.
Is there a way to check is f has a valid functor which would mean f is a lambda?
Is there a better solution to what I am trying to achieve?
What you want is not possible (or a good idea, but nevermind that now).
A "function" by name in C++ represents potentially many functions. It represents overloads, template instantiations through template argument deduction, etc. But to get a function pointer, you are required to peer through all of that. If the name represents an overload set, to get a pointer, you have to cast that name to a specific overload. If the name represents a template, you have to provide the template arguments to denote a specific instantiation.
What that means is that, by the time your hypothetical is_callable concept gets to be invoked on a function pointer type, all of the overload resolution and template substitution has already happened. It is given a single pointer to a specific, well-defined piece of code which can be called with a signature defined by the type of that pointer.
None of that is the case for a function object. A functor (whether generated by a C++ lambda expression or just a hand-written type) is nothing more than a type with an operator() overload. And that overload is just a function name, exactly like any other name: subject to rules of overload resolution and template substitution.
C++ does not allow you to ask the question "here's a name; can I call this with something?"
And broadly speaking, that's just not a useful question to ask.
Whether you're using this "callable" concept for currying or whathaveyou, at some point, some piece of code is going to call some function with some set of arguments, which will eventually cascade into calling the given function with another set of argument defined by some process. That is the point when you need to constrain the given callable.
Constraining the function at the site where you're building the curried callable is useless. You have no idea if there's a type mismatch between arguments and return values, or anything of the kind. You will only know that when you're given a set of arguments to use to call the curried callable. That's the place when you can compute the arguments to eventually call the proper function, so that's where the constraining should happen.
I was watching Dr. Walter E. Brown's Template Meta-programming talk. In his presentation he presents code like so for is_copy_assignable:
template<class U, class = decltype(declval<U&>() = declval<U const&>())>
static true_type try_assignment(U &&);
What I am having trouble with is how is the assignment operator being called in this instance. When I try and reason about this code and say substitute a dummy type, say int, in place of the U I get:
template<class U, class = decltype(declval<int&>() = declval<int const&>())>
template<class U, class = decltype(int& = int const&)>
So I am wondering then how can this help us determine if the assignment operator is valid. If I understand declval correctly this code will not even evaluate so then how can one determine from int& = int const&, which doesn't even evaluate, if there is or isn't a assignment operator defined for U.
I understand that in most cases the copy-assignment operator would be defined as
C& operator=(const C& other)
which looks a lot like what is above, but still since nothing is being evaluated then what use is this information.
I don't really follow what you intended in performing the following step:
template<class U, class = decltype(declval<int&>() = declval<int const&>())>
template<class U, class = decltype(int& = int const&)>
declval says: pretend to return a value of type of the template argument. I say pretend because the function isn't really defined, only declared, so it can only be used in an unevaluated context. Inside of decltype is an unevaluated contexts because you're only checking types.
So, the expression decltype(declval<int&>() = declval<int const&>()) is basically saying: if I had an int&, call it x, and I had a int const&, call it y, what is the type of the expression x = y?
As you can probably guess this calls the assignment operator and this expression will have a valid type (in this case, int&). If you change int to unique_ptr, this expression won't have a valid type, because unique_ptr is not assignable, causing type substitution to fail and eliminating this template from the overload or specialization set.
The reason to have declval at all is because it lets you create a value of any type; this is particularly useful for a) creating something of reference type, and b) creating non-reference types without assuming that they have e.g. a default constructor. Thus, use of declval is extremely ubiquitous in high quality TMP.
If I have a class
class A {
template<class T>
void foo(T t);
};
what is the difference / the way of the book to call foo?
a.foo(t)
or
a.foo<X>(t)
?
a.foo(t) will basically deduce T to the type of t. If this is fine with you, use this.
a.foo<X> will force T to be X and the correctness of the expression now depends on if t is convertible to X. Sometimes you desire this, if you want t to be converted to X, but you could always write this as a.foo(X{t}). The second form is not entirely equivalent, since it also allows explicit conversions.
In the second variant, X may not be the type of t. E.g., if t is a float, but you say a.foo<int>(t), no floating variant will be deduced, but rather the integer-variant (if not exists already), and a float-to-int conversion will happen.
If the types are identical, there is no difference. However, one never knows for the whole lifetime of the code whether the types never change.