-
Notifications
You must be signed in to change notification settings - Fork 614
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement C++ style functors as targets for objectives #457
Implement C++ style functors as targets for objectives #457
Conversation
I'd add, I didn't implement any tests because I didn't find a consistent style, there's one file named Anyway, however I tested this in my own code: it compiles and runs OK. |
This is way better than the old way. I'd say create another test: t_tutorial.cxx aims at reproducing the example from the documentation. |
@jschueller Done. Wrote a macro and a (somewhat) comprehensive test for the new syntax. |
Hm, windows build failed, says cannot find test executables... I'm sure it's my inability to write proper CMake code (: could you help me debug this one please? |
I looked at the failing log output again, and I can't get it why it failed. It says that |
the compilation fails at the swig level:
|
I suspect it was because I wrote a function |
bump |
Could you guys merge this in please? if everything looks OK to you |
bumping it again |
Hey, could you re-run the CI please? |
OK, does everything look good now? |
Hello?.. 🥲 |
hi! any progress on this? |
|
||
|
||
// linear regression optimization | ||
optimizer.set_min_objective(std::move(objective_1)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My understanding is that std::move
leaves the source object (objective_1
) in a "valid but undefined state", which seems like not the sort of thing one would always want to do — what if the caller wants to use the objective function for something else in addition to optimization?
In general, we should be clear about the resource management here. Who is responsible for freeing the resources associated with the objective functor? Do they get freed when the functor leaves the scope where it was defined, or do they get freed when the optimizer
object is destroyed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this example, I simply used std::move
just because I myself don't need the objective_1
afterwards in the main
function, so it's better to move it rather than copy. But in practice, users should decide for themselves what they want: to make a copy or move the object. Since set_min_objective
accepts the parameter by value, it will make a copy of the argument unless it's passed using std::move
(or is an otherwise RHS expression, like nameless lambda). OK, this is a slight simplification because in reality std::function
's constructor is called in between, but the copy-or-move logic is still intact -- because std::function
has a move ctor.
In general, since set_min_objective
, set_max_objective
accept a functor by value, it is assumed that optimizer
is responsible for memory management of its own copy of the functor, but the outer one should be managed by the user. I believe this is fairly common, and actually the std::move
allows the user to decide whether s/he wants to keep the object or pass it altogether, instead of hardcoding this into a library. Again, in this example I chose to "move" the object because I just don't need it afterwards. But we can also write simply
optimizer.set_min_objective(objective_1);
and then the objective_1
is usable (and a copy is made).
As for the inner workings, since functor is an STL-container, functor will get destroyed when std::function
's destructor is called. Which, in turn, happens when myfunc_data
is freed. And that, in turn, is already well-designed within the library.
Do you agree with this approach?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That seems fine to me. We shouldn't use std::move
in the documentation examples, however, because people are likely to copy that blindly and will then get into trouble.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh, okay! I could change that if you want? in a new PR? and thank you for merging!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A new PR to clarify this would be fine.
What is Edit: Ahhh |
#ifdef NLOPT_CXX11 | ||
"AGS (global, no-derivative)" | ||
#else | ||
"AGS (NOT COMPILED)" | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR broke nlopt_algorithm_name
as it relied on this ordering that was changed...
"AGS (global, no-derivative)" | ||
#else | ||
"StoGO (NOT COMPILED)", | ||
"StoGO randomized (NOT COMPILED)", | ||
"AGS (NOT COMPILED)" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The missing ,
at the end of these items cause them to be merged with the item after which causes there to be one too few items in this array.
This PR implements a wrapper
nlopt::functor_wrapper
for C++ style functors viastd::function
, and two new overloads ofnlopt::set_min_objective
,nlopt::set_max_objective
.In order to allow that, a new member field in the
myfunc_data
struct is added:functor_type functor;
, wherefunctor_type
is defined asThis is not introduced as a pointer (like the other function-pointers are) because
std::function
is already a container that stores a pointer, and abstracts it away.Important: note that the signature for the functor does not include
void* data
unlike all other function-pointers. That is because it is assumed that the functor already has all the data it needs.This PR allows now to write the following:
Same with C++ lambdas, regular functions and even class member functions (check out std::function).
This PR also introduces a CMake macro
NLOPT_add_cpp_test
to quickly add cpp tests, and creates a testcpp_functor.cxx
to actually test the new functionality.Closes #219 .