fix: pass everything by copy in forges

- Only way to avoid references to out-of-scope variables.
- Add warnings everywhere in documentation to enforce using std::ref.
This commit is contained in:
Johann Dreo 2020-04-27 21:40:35 +02:00
commit 23f6e68d34
2 changed files with 43 additions and 12 deletions

View file

@ -39,15 +39,19 @@
* foundry.selectors.add< eoStochTournamentSelect<EOT> >( 0.5 ); * foundry.selectors.add< eoStochTournamentSelect<EOT> >( 0.5 );
* @endcode * @endcode
* *
* @warning If the constructor takes a reference YOU SHOULD ABSOLUTELY wrap it
* in a `std::ref`, or it will silently be passed as a copy,
* which would effectively disable any link between operators.
*
* In a second step, the operators to be used should be selected * In a second step, the operators to be used should be selected
* by indicating their index, just like the foundry was a array of five elements: * by indicating their index, just like the foundry was a array of five elements:
* @code * @code
* foundry = {0, 1, 2, 0, 3}; * foundry = {0, 1, 2, 0, 3};
* // ^ continue * // ^ ^ ^ ^ ^ replacement
* // ^ crossover * // | | | + selection
* // ^ mutation * // | | + mutation
* // ^ selection * // | + crossover
* // ^ replacement * // + continue
* @endcode * @endcode
* *
* @note: by default, the firsts of the five operators are selected. * @note: by default, the firsts of the five operators are selected.

View file

@ -83,6 +83,11 @@ class eoForgeInterface
* *
* This allows for creating containers of pre-parametrized operators (@see eoForgeVector). * This allows for creating containers of pre-parametrized operators (@see eoForgeVector).
* *
* @warning When passing a reference (as it is often the case within ParadisEO),
* it is MANDATORY to wrap it in `std::ref`, or else it will default to use copy.
* This is is a source of bug which your compiler will to detect and that would
* disable any link between operators.
*
* @code * @code
eoForgeOperator<eoselect<EOT>,eoRankMuSelect<EOT>> forge(mu); eoForgeOperator<eoselect<EOT>,eoRankMuSelect<EOT>> forge(mu);
// ^ desired ^ to-be-instanciated ^ operator's // ^ desired ^ to-be-instanciated ^ operator's
@ -101,8 +106,10 @@ template<class Itf, class Op, typename... Args>
class eoForgeOperator : public eoForgeInterface<Itf> class eoForgeOperator : public eoForgeInterface<Itf>
{ {
public: public:
eoForgeOperator(Args&&... args) : // Use an additional template to avoid redundant copies of decayed Args variadic.
_args(std::forward<Args>(args)...), template<class ...Args2>
eoForgeOperator(Args2... args) :
_args(std::forward<Args2>(args)...),
_instanciated(nullptr) _instanciated(nullptr)
{ } { }
@ -131,7 +138,7 @@ class eoForgeOperator : public eoForgeInterface<Itf>
} }
protected: protected:
std::tuple<Args&&...> _args; std::tuple<Args...> _args;
private: private:
/** Metaprogramming machinery which deals with arguments lists @{ */ /** Metaprogramming machinery which deals with arguments lists @{ */
@ -182,6 +189,11 @@ class eoForgeOperator<Itf,Op> : public eoForgeInterface<Itf>
* @note You can actually store several instances of the same class, * @note You can actually store several instances of the same class,
* with different parametrization (or not). * with different parametrization (or not).
* *
* @warning When passing a reference (as it is often the case within ParadisEO),
* it is MANDATORY to wrap it in `std::ref`, or else it will default to use copy.
* This is is a source of bug which your compiler will to detect and that would
* disable any link between operators.
*
* @warning You may want to enable instanciation cache to grab some performances. * @warning You may want to enable instanciation cache to grab some performances.
* The default is set to disable the cache, because its use with operators * The default is set to disable the cache, because its use with operators
* which hold a state will lead to unwanted behaviour. * which hold a state will lead to unwanted behaviour.
@ -225,11 +237,20 @@ class eoForgeVector : public std::vector<eoForgeInterface<Itf>*>
{ } { }
/** Add an operator to the list. /** Add an operator to the list.
*
* @warning When passing a reference (as it is often the case within ParadisEO),
* it is MANDATORY to wrap it in `std::ref`, or else it will default to use copy.
* This is is a source of bug which your compiler will to detect and that would
* disable any link between operators.
*
*/ */
template<class Op, typename... Args> template<class Op, typename... Args>
void add(Args&&... args) void add(Args... args)
{ {
auto pfo = new eoForgeOperator<Itf,Op,Args...>(std::forward<Args>(args)...); // We decay all args to ensure storing everything by value within the forge.
// The references should thus be wrapped in a std::ref.
auto pfo = new eoForgeOperator<Itf,Op,std::decay_t<Args>...>(
std::forward<Args>(args)...);
this->push_back(pfo); this->push_back(pfo);
} }
@ -243,15 +264,21 @@ class eoForgeVector : public std::vector<eoForgeInterface<Itf>*>
} }
/** Change the set up arguments to the constructor. /** Change the set up arguments to the constructor.
*
* @warning When passing a reference (as it is often the case within ParadisEO),
* it is MANDATORY to wrap it in `std::ref`, or else it will default to use copy.
* This is is a source of bug which your compiler will to detect and that would
* disable any link between operators.
* *
* @warning The operator at `index` should have been added with eoForgeVector::add already.. * @warning The operator at `index` should have been added with eoForgeVector::add already..
*/ */
template<class Op, typename... Args> template<class Op, typename... Args>
void setup(size_t index, Args&&... args) void setup(size_t index, Args... args)
{ {
assert(index < this->size()); assert(index < this->size());
delete this->at(index); // Silent on nullptr. delete this->at(index); // Silent on nullptr.
auto pfo = new eoForgeOperator<Itf,Op,Args...>(std::forward<Args>(args)...); auto pfo = new eoForgeOperator<Itf,Op,std::decay_t<Args>...>(
std::forward<Args>(args)...);
this->at(index) = pfo; this->at(index) = pfo;
} }