Derniers messages sur Zeste de Savoirhttps://zestedesavoir.com/forums/2023-01-22T14:40:58+01:00Les derniers messages parus sur le forum de Zeste de Savoir.many_cast : passer de std::vector<std::any> à une décomposition, message #2485142023-01-22T14:40:58+01:00jo_link_noir/@jo_link_noirhttps://zestedesavoir.com/forums/sujet/16762/many_cast-passer-de-stdvectorstdany-a-une-decomposition/?page=1#p248514<p>Pour moi,l’utilisation de <code>.at()</code> est à proscrire. Déjà, parce que permettre un dépassement ne devrait pas arriver et ensuite parce qu’une exception aussi générique que <code>std::out_of_range</code> n’apporte aucun contexte exploitable. Le seul endroit où c’est exploitable est au moment de l’appel, sauf qu’à ce moment, vérifier la taille est beaucoup moins verbeux qu’un système d’exception (en plus d’être bien mieux localisé d’un <code>try</code>/<code>catch</code> qui englobe un bloc plus large).</p>
<p>Du coup, la vérification devrait se faire dans many_cast avec un type d’exception propre. En général je fais 2 versions <code>unsafe_foo()</code>/<code>foo()</code> ou <code>unsafe_foo()</code>/<code>foo()</code> en fonction du besoin visé. S’il y a beaucoup d’option possible, j’ajoute un premier paramètre optionnel qui active / désactive certains comportements (comme on le ferrait avec une classe de trait ou de politique).</p>
<p>Pour <code>std::bad_any_cast</code>, il faut voir ce que tu veux en faire, mais si c’est pour remplacer un type incorrect par une valeur, je pense que le plus simple est de wrapper le paramètre dans une sorte de <code>value_or<T, F></code> qui utiliserait <code>F</code> si <code>std::any_cast<T>(any*)</code> retourne <code>nullptr</code>.</p>
<blockquote>
<p>Autre difficulté, many_cast fait systématiquement une copie des paramètres pour les extraire.</p>
</blockquote>
<p>Pour éviter cela, il faudrait propager le type de référence à <code>mcimpl</code> et prendre des <code>auto&& params</code>. C’est aussi possible de faire comme avec ma solution value_or plus haut pour le faire paramètre par paramètre.</p>
<p>Concernant <code>std::make_tuple</code>, ne pas conserver les références est documenté. Le mieux est de directement faire <code>std::tuple<Args...></code>. Mais ici, le type de retour de tous les any_cast est <code>T</code>, std::tuple utilisera toujours un déplacement pour construire ses valeurs.</p>
<p>Un code d’exemple value_or (j’ai aussi comment faire une seule fonction pour many_cast).</p>
<div class="hljs-code-div hljs-code-cpp"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span><span data-count="6"></span><span data-count="7"></span><span data-count="8"></span><span data-count="9"></span><span data-count="10"></span><span data-count="11"></span><span data-count="12"></span><span data-count="13"></span><span data-count="14"></span><span data-count="15"></span><span data-count="16"></span><span data-count="17"></span><span data-count="18"></span><span data-count="19"></span><span data-count="20"></span><span data-count="21"></span><span data-count="22"></span><span data-count="23"></span><span data-count="24"></span><span data-count="25"></span><span data-count="26"></span><span data-count="27"></span><span data-count="28"></span><span data-count="29"></span><span data-count="30"></span><span data-count="31"></span><span data-count="32"></span><span data-count="33"></span><span data-count="34"></span><span data-count="35"></span><span data-count="36"></span><span data-count="37"></span><span data-count="38"></span><span data-count="39"></span><span data-count="40"></span><span data-count="41"></span><span data-count="42"></span><span data-count="43"></span><span data-count="44"></span><span data-count="45"></span><span data-count="46"></span><span data-count="47"></span><span data-count="48"></span><span data-count="49"></span><span data-count="50"></span><span data-count="51"></span><span data-count="52"></span><span data-count="53"></span><span data-count="54"></span><span data-count="55"></span><span data-count="56"></span><span data-count="57"></span><span data-count="58"></span><span data-count="59"></span><span data-count="60"></span><span data-count="61"></span><span data-count="62"></span><span data-count="63"></span><span data-count="64"></span><span data-count="65"></span><span data-count="66"></span><span data-count="67"></span><span data-count="68"></span><span data-count="69"></span><span data-count="70"></span><span data-count="71"></span></div><pre><code class="hljs language-cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><any></span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><vector></span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><tuple></span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><string></span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><iostream></span></span>
<span class="hljs-keyword">template</span><<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>></span>
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">mc_value</span>
{</span>
<span class="hljs-function"><span class="hljs-keyword">template</span><class Any>
<span class="hljs-keyword">decltype</span>(<span class="hljs-keyword">auto</span>) <span class="hljs-title">operator</span><span class="hljs-params">()</span><span class="hljs-params">(Any&& any)</span>
</span>{
<span class="hljs-keyword">return</span> std::any_cast<T>(<span class="hljs-keyword">static_cast</span><Any&&>(any));
}
};
<span class="hljs-keyword">template</span><<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>, <span class="hljs-title">auto</span> <span class="hljs-title">make</span>></span>
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">mc_value_or</span>
{</span>
<span class="hljs-function">T <span class="hljs-title">operator</span><span class="hljs-params">()</span><span class="hljs-params">(<span class="hljs-keyword">auto</span>* any)</span>
</span>{
<span class="hljs-keyword">auto</span>* p = std::any_cast<T>(any);
<span class="hljs-keyword">return</span> p ? *p : <span class="hljs-built_in">make</span>();
}
<span class="hljs-function">T <span class="hljs-title">operator</span><span class="hljs-params">()</span><span class="hljs-params">(<span class="hljs-keyword">auto</span>& any)</span>
</span>{
<span class="hljs-keyword">return</span> <span class="hljs-built_in"><span class="hljs-keyword">operator</span></span>()(&any);
}
};
<span class="hljs-function"><span class="hljs-keyword">template</span><class T>
T <span class="hljs-title">make_default</span><span class="hljs-params">()</span>
</span>{
<span class="hljs-keyword">return</span> <span class="hljs-built_in">T</span>();
}
<span class="hljs-keyword">template</span><<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>></span>
<span class="hljs-keyword">using</span> mc_value_or_default = mc_value_or<T, make_default<T>>;
<span class="hljs-keyword">template</span><<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>></span>
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">to_mc_value</span>
{</span>
<span class="hljs-keyword">using</span> type = mc_value<T>;
};
<span class="hljs-keyword">template</span><<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>, <span class="hljs-title">auto</span> <span class="hljs-title">make</span>></span>
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">to_mc_value</span><</span>mc_value_or<T, make>>
{
<span class="hljs-keyword">using</span> type = mc_value_or<T, make>;
};
<span class="hljs-keyword">template</span><<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>></span>
<span class="hljs-keyword">using</span> <span class="hljs-keyword">to_mc_value_t</span> = <span class="hljs-keyword">typename</span> to_mc_value<T>::type;
<span class="hljs-function"><span class="hljs-keyword">template</span><class... Args>
<span class="hljs-keyword">auto</span> <span class="hljs-title">many_cast</span><span class="hljs-params">(<span class="hljs-keyword">auto</span>& params)</span>
</span>{
<span class="hljs-keyword">return</span> [&]<std::<span class="hljs-keyword">size_t</span>... I>(std::index_sequence<I...>, <span class="hljs-keyword">auto</span>... casts) {
<span class="hljs-keyword">return</span> std::tuple<<span class="hljs-keyword">decltype</span>(<span class="hljs-built_in">casts</span>(params[I]))...>{ <span class="hljs-built_in">casts</span>(params[I])... };
}(std::make_index_sequence<<span class="hljs-keyword">sizeof</span>...(Args)>(), <span class="hljs-keyword">to_mc_value_t</span><Args>()...);
}
<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span>
</span>{
std::vector<std::any> params{<span class="hljs-number">42</span>, <span class="hljs-number">3.14</span>, std::string{<span class="hljs-string">"bar"</span>}, std::<span class="hljs-built_in">any</span>()};
<span class="hljs-keyword">auto</span> [answer, pi, name, x] = many_cast<<span class="hljs-keyword">int</span>, <span class="hljs-keyword">double</span>, std::string, mc_value_or_default<<span class="hljs-keyword">int</span>>>(params);
std::cout << answer << <span class="hljs-string">" "</span> << pi << <span class="hljs-string">" "</span> << name << <span class="hljs-string">" "</span> << x << <span class="hljs-string">"\n"</span>;
}
</code></pre></div>many_cast : passer de std::vector<std::any> à une décomposition, message #2485132023-01-22T13:08:38+01:00germinolegrand/@germinolegrandhttps://zestedesavoir.com/forums/sujet/16762/many_cast-passer-de-stdvectorstdany-a-une-decomposition/?page=1#p248513<p>J’ai bien conscience que c’est un peu avancé comme interrogation et que l’habitude des choses voudrait que j’aille la poser sur un forum anglais, mais d’une part : je ne sais pas lequel ; d’autre part : j’aimerais bien qu’on stimule un peu plus la communauté C++ de zeste de savoir et francophone en général <img src="/static/smileys/svg/smile.svg" alt=":)" class="smiley"></p>
<h3 id="mon-cas-dusage">Mon cas d’usage<a aria-hidden="true" tabindex="-1" href="#mon-cas-dusage"><span class="icon icon-link"></span></a></h3>
<p>J’ai une interface par fonction membre virtuelle que je redéfinis pour un cas concret (si ça vous intéresse, ça vient d’ici : <a href="https://gitlab.com/inendi/inspector/">https://gitlab.com/inendi/inspector/</a> ).</p>
<p>Pour tous les paramètres inconnus de la classe abstraite, il y a un <code>std::vector<std::any>></code>.</p>
<div class="hljs-code-div hljs-code-cpp"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span><span data-count="6"></span><span data-count="7"></span><span data-count="8"></span><span data-count="9"></span><span data-count="10"></span><span data-count="11"></span></div><pre><code class="hljs language-cpp"><span class="hljs-keyword">using</span> Params = std::vector<std::any>;
QWidget* PVDisplays::PVDisplayViewGroupBy::<span class="hljs-built_in">create_widget</span>(Inendi::PVView* view,
QWidget* parent,
Params <span class="hljs-keyword">const</span>& params) <span class="hljs-keyword">const</span>
{
<span class="hljs-keyword">auto</span> col1 = std::any_cast<PVCol>(params.<span class="hljs-built_in">at</span>(<span class="hljs-number">0</span>));
<span class="hljs-keyword">auto</span> col2 = std::any_cast<PVCol>(params.<span class="hljs-built_in">at</span>(<span class="hljs-number">1</span>));
<span class="hljs-keyword">auto</span> operation = std::any_cast<Operation>(params.<span class="hljs-built_in">at</span>(<span class="hljs-number">2</span>));
<span class="hljs-comment">//...</span>
}
</code></pre></div>
<p>Ce code me paraît redondant, sujet aux erreurs (notamment d’indice) et peu agréable à lire (il y a pire me direz-vous).</p>
<h3 id="many_cast">many_cast<a aria-hidden="true" tabindex="-1" href="#many_cast"><span class="icon icon-link"></span></a></h3>
<p>J’ai donc essayé de simplifier l’usage avec un <code>many_cast</code> calqué sur <code>std::any_cast</code>.</p>
<p>J’ai essayé de le faire en une seule fonction, mais je n’y suis pas arrivé ; si vous avez une meilleure solution, je suis preneur !</p>
<div class="hljs-code-div hljs-code-cpp"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span><span data-count="6"></span><span data-count="7"></span><span data-count="8"></span><span data-count="9"></span><span data-count="10"></span><span data-count="11"></span></div><pre><code class="hljs language-cpp"><span class="hljs-function"><span class="hljs-keyword">template</span><class... Args, std::<span class="hljs-keyword">size_t</span>... I>
<span class="hljs-keyword">auto</span> <span class="hljs-title">mcimpl</span><span class="hljs-params">(<span class="hljs-keyword">auto</span>& params, std::index_sequence<I...>)</span>
</span>{
<span class="hljs-keyword">return</span> std::<span class="hljs-built_in">make_tuple</span>(std::any_cast<Args>(params.<span class="hljs-built_in">at</span>(I))...);
}
<span class="hljs-keyword">template</span><class... Args, <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Seq</span> =</span> std::make_index_sequence<<span class="hljs-keyword">sizeof</span>...(Args)>>
<span class="hljs-function"><span class="hljs-keyword">auto</span> <span class="hljs-title">many_cast</span><span class="hljs-params">(<span class="hljs-keyword">auto</span>& params)</span>
</span>{
<span class="hljs-keyword">return</span> mcimpl<Args...>(params, Seq{});
}
</code></pre></div>
<p>Ce qui donne à l’usage :</p>
<div class="hljs-code-div hljs-code-cpp"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span><span data-count="6"></span><span data-count="7"></span><span data-count="8"></span><span data-count="9"></span></div><pre><code class="hljs language-cpp"><span class="hljs-keyword">using</span> Params = std::vector<std::any>;
QWidget* PVDisplays::PVDisplayViewGroupBy::<span class="hljs-built_in">create_widget</span>(Inendi::PVView* view,
QWidget* parent,
Params <span class="hljs-keyword">const</span>& params) <span class="hljs-keyword">const</span>
{
<span class="hljs-keyword">auto</span> [col1, col2, operation] = many_cast<PVCol, PVCol, Operation>(params);
<span class="hljs-comment">//...</span>
}
</code></pre></div>
<p>Côté usage, ça me semble bien mieux : plus d’erreur possible dans les indices, et un one-liner.</p>
<p>Si d’aventure j’avais un nombre de paramètres dynamique, ils restent accessibles, ce n’est donc pas intrusif.</p>
<p>J’ai utilisé un <code>std::tuple</code>, mais comme on le voit dans l’utilisation, ça reste un détail d’implémentation. Si d’autres alternatives vous viennent, je suis intéressé.</p>
<h4 id="les-exceptions">Les exceptions<a aria-hidden="true" tabindex="-1" href="#les-exceptions"><span class="icon icon-link"></span></a></h4>
<p>Là où ça se corse, c’est si je cherche à gérer les exceptions. J’hésite à faire une gestion différente entre <code>many_cast</code> et <code>std::any_cast</code>. Les deux types d’exceptions potentielles à gérer sont <code>std::bad_any_cast</code> et <code>std::out_of_range</code>. Il y a bien évidemment toutes les exceptions des constructeurs de copie, mais il me semble que cela n’a pas d’impact particulier (et je ne vois pas l’intérêt de les gérer).</p>
<h4 id="copie-uniquement">Copie uniquement<a aria-hidden="true" tabindex="-1" href="#copie-uniquement"><span class="icon icon-link"></span></a></h4>
<p>Autre difficulté (qui n’en est pas une dans mon cas d’usage, mais pourrait l’être si je réutilise <code>many_cast</code> dans un autre contexte), <code>many_cast</code> fait systématiquement une copie des paramètres pour les extraire. </p>
<p><code>std::any_cast</code> est plus subtil, il permet de récupérer par référence, ou par déplacement.</p>
<p>Je ne suis du tout confiant dans la robustesse de <code>many_cast</code> pour ce type d’accès, qui plus est potentiellement mixé : est-ce que le passage via <code>std::make_tuple</code>, qui est un détail d’implémentation, ne rend pas impossible de récupérer des références ?</p>
<h3 id="code-générique">Code générique<a aria-hidden="true" tabindex="-1" href="#code-générique"><span class="icon icon-link"></span></a></h3>
<p>Pour jouer avec et préparer une généralisation :</p>
<div class="hljs-code-div hljs-code-cpp"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span><span data-count="6"></span><span data-count="7"></span><span data-count="8"></span><span data-count="9"></span><span data-count="10"></span><span data-count="11"></span><span data-count="12"></span><span data-count="13"></span><span data-count="14"></span><span data-count="15"></span><span data-count="16"></span><span data-count="17"></span><span data-count="18"></span><span data-count="19"></span><span data-count="20"></span><span data-count="21"></span><span data-count="22"></span><span data-count="23"></span></div><pre><code class="hljs language-cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><any></span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><vector></span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><tuple></span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><string></span></span>
<span class="hljs-function"><span class="hljs-keyword">template</span><class... Args, std::<span class="hljs-keyword">size_t</span>... I>
<span class="hljs-keyword">auto</span> <span class="hljs-title">mcimpl</span><span class="hljs-params">(<span class="hljs-keyword">auto</span>& params, std::index_sequence<I...>)</span>
</span>{
<span class="hljs-keyword">return</span> std::<span class="hljs-built_in">make_tuple</span>(std::any_cast<Args>(params.<span class="hljs-built_in">at</span>(I))...);
}
<span class="hljs-keyword">template</span><class... Args, <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Seq</span> =</span> std::make_index_sequence<<span class="hljs-keyword">sizeof</span>...(Args)>>
<span class="hljs-function"><span class="hljs-keyword">auto</span> <span class="hljs-title">many_cast</span><span class="hljs-params">(<span class="hljs-keyword">auto</span>& params)</span>
</span>{
<span class="hljs-keyword">return</span> mcimpl<Args...>(params, Seq{});
}
<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span>
</span>{
std::vector<std::any> params{<span class="hljs-number">42</span>, <span class="hljs-number">3.14</span>, std::string{<span class="hljs-string">"bar"</span>}};
<span class="hljs-keyword">auto</span> [answer, pi, name] = many_cast<<span class="hljs-keyword">int</span>, <span class="hljs-keyword">double</span>, std::string>(params);
}
</code></pre></div>
<p>Hâte de lire vos réflexions <img src="/static/smileys/svg/smile.svg" alt=":)" class="smiley"></p>[C++] Factory d'une classe qui spécialise une classe mère template, message #2366372021-08-13T16:22:50+02:00cvanaret/@cvanarethttps://zestedesavoir.com/forums/sujet/15589/c-factory-dune-classe-qui-specialise-une-classe-mere-template/?page=1#p236637<p>Ca fonctionne, merci <img src="/static/smileys/svg/smile.svg" alt=":)" class="smiley"><br>
C’est futé, d’exposer le type <code>using matrix_type = MatrixType;</code>.</p>[C++] Factory d'une classe qui spécialise une classe mère template, message #2366322021-08-13T15:05:42+02:00ads00/@ads00https://zestedesavoir.com/forums/sujet/15589/c-factory-dune-classe-qui-specialise-une-classe-mere-template/?page=1#p236632<p>D’après les infos que tu donnes, je ferais un truc dans ce style : </p>
<div class="hljs-code-div hljs-code-cpp"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span><span data-count="6"></span><span data-count="7"></span><span data-count="8"></span><span data-count="9"></span><span data-count="10"></span><span data-count="11"></span><span data-count="12"></span><span data-count="13"></span><span data-count="14"></span><span data-count="15"></span><span data-count="16"></span><span data-count="17"></span><span data-count="18"></span><span data-count="19"></span><span data-count="20"></span><span data-count="21"></span><span data-count="22"></span><span data-count="23"></span><span data-count="24"></span><span data-count="25"></span><span data-count="26"></span><span data-count="27"></span></div><pre><code class="hljs language-cpp"><span class="hljs-keyword">template</span><<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Solver</span>, <span class="hljs-title">template</span><</span><span class="hljs-class"><span class="hljs-keyword">class</span>></span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Hessian</span>></span>
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">system</span>
{</span>
Hessian<Solver::matrix_type> hessian_;
Solver solver_;
};
<span class="hljs-keyword">template</span><<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MatrixType</span>></span>
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">hessianA</span>
{</span>
MatrixType matrix_;
};
<span class="hljs-keyword">template</span><<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MatrixType</span>></span>
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">basic_solver</span>
{</span>
<span class="hljs-keyword">using</span> matrix_type = MatrixType;
<span class="hljs-function">virutal <span class="hljs-keyword">void</span> <span class="hljs-title">factorize</span><span class="hljs-params">(MatrixType)</span> </span>= <span class="hljs-number">0</span>;
};
<span class="hljs-comment">// concepts si C++20</span>
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">solverA</span> :</span> basic_solver<MatrixA>
{
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">factorize</span><span class="hljs-params">(MatrixA)</span> </span>{}
};
<span class="hljs-comment">//</span>
system<MA57Solver, ExactHessian>
</code></pre></div>[C++] Factory d'une classe qui spécialise une classe mère template, message #2366272021-08-13T12:54:06+02:00cvanaret/@cvanarethttps://zestedesavoir.com/forums/sujet/15589/c-factory-dune-classe-qui-specialise-une-classe-mere-template/?page=1#p236627<p>Cool, ta fonction <code>solve_system()</code> paramétrée par la matrice et le solveur est intéressante.
Par contre, j’ai oublié de préciser un truc : on va résoudre une <strong>séquence</strong> de systèmes linéaires, pas un seul. La méthode <code>InteriorPointMethod::compute_direction()</code> va être appelée plusieurs fois. C’est pour ça que le solveur est un membre de <code>InteriorPointMethod</code>.</p>
<p>Quoi qu’il en soit, ta suggestion est intéressante. Je voulais que la classe abstraite <code>LinearSolver</code> déclare <code>factorize()</code>, mais comme tu l’as montré, chaque sous-classe peut déclarer sa propre <code>factorize(mon_type_de_matrice)</code>. Je vais y réfléchir.</p>
<figure><blockquote>
<p>je me demande aussi si les classes solver ne peuvent pas être réduit à une fonction, car si leur seule responsabilité est de résoudre un système, je crois que ce n’est qu’une séquence d’opération qui s’applique sur les arguments d’entrée, il n’y a pas besoin d’état interne ou je ne sais quoi, si ?</p>
</blockquote><figcaption><a href="https://zestedesavoir.com/forums/sujet/15589/c-factory-dune-classe-qui-specialise-une-classe-mere-template/?page=1#p236626">romantik</a></figcaption></figure>
<p>Mes classes Solver allouent des tableaux et manipulent pas mal de trucs en interne, donc non, elles ne peuvent pas être réduites à une fonction.</p>
<figure><blockquote>
<p>Je rejoins ads00, tu nous expliques comment tu l’as conçu mais pas pourquoi, du coup difficile de faire des choix de design différent<br>
</p>
</blockquote><figcaption><a href="https://zestedesavoir.com/forums/sujet/15589/c-factory-dune-classe-qui-specialise-une-classe-mere-template/?page=1#p236626">romantik</a></figcaption></figure>
<p>Je fais de la recherche dans le domaine des méthodes d’optimisation et je développe un framework qui implémente des composants génériques qu’on peut combiner à la volée. L’idée est d’obtenir automatiquement les combinaisons :</p>
<p>{solveurs MA57, PARDISO} x {stratégies Hessienne} x {interior point, autres méthodes} x {autres trucs}</p>
<p>Si ça vous intéresse (mais ça rentre dans les détails mathématiques), j’ai donné une présentation dans une conf en juillet pour décrire les possibilités du framework : <a href="https://www.researchgate.net/publication/353418272">https://www.researchgate.net/publication/353418272</a></p>
<p>Clairement le projet est ambitieux et je suis un peu limité parfois <img src="/static/smileys/svg/langue.svg" alt=":p" class="smiley"> Mais je suis presque au bout.</p>[C++] Factory d'une classe qui spécialise une classe mère template, message #2366262021-08-13T12:20:12+02:00romantik/@romantikhttps://zestedesavoir.com/forums/sujet/15589/c-factory-dune-classe-qui-specialise-une-classe-mere-template/?page=1#p236626<p>J’ai de plus en plus l’impression que tu essaies de mettre de la généricité là où il n’y en a pas, puisque tu dis toi-même : </p>
<blockquote>
<p><code>LinearSolver<MatrixType></code>, <code>HessianEvaluation<MatrixType></code> et <code>InteriorPointMethod<MatrixType></code> doivent être créés avec le même paramètre de template, ce qui permet de "fixer un unique format de matrice creuse dans tout le code".</p>
</blockquote>
<p>Et je ne fais pas de maths assez pointues pour manipuler ce genre d’objets mathématiques tous les jours, mais je me demande aussi si les classes solver ne peuvent pas être réduit à une fonction, car si leur seule responsabilité est de résoudre un système, je crois que ce n’est qu’une séquence d’opération qui s’applique sur les arguments d’entrée, il n’y a pas besoin d’état interne ou je ne sais quoi, si ?</p>
<p>Je rejoins ads00, tu nous expliques comment tu l’as conçu mais pas pourquoi, du coup difficile de faire des choix de design différent<br>
Et de ce que je comprends SolvA n’est capable de traiter que les matriceA et solvB que les matriceB mais tu veux les rendre generiques parce que tu vas avoir un code en fonction de ta config et tu veux faire</p>
<div class="hljs-code-div hljs-code-cpp"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span><span data-count="6"></span><span data-count="7"></span><span data-count="8"></span><span data-count="9"></span><span data-count="10"></span><span data-count="11"></span><span data-count="12"></span><span data-count="13"></span><span data-count="14"></span><span data-count="15"></span></div><pre><code class="hljs language-cpp"><span class="hljs-built_in"><span class="hljs-keyword">switch</span></span>(config)
{
<span class="hljs-keyword">case</span> configA:
MatA mat{...};
SolvA solv{...};
solv.<span class="hljs-built_in">factorize</span>(A);
[...]
<span class="hljs-keyword">break</span>;
<span class="hljs-keyword">case</span> configB:
MatB mat{...};
SolvB solv{...};
solv.<span class="hljs-built_in">factorize</span>(mat);
[...] <span class="hljs-comment">// soit exactement le même code que le case configA mais avec des types différents</span>
<span class="hljs-keyword">break</span>;
}
</code></pre></div>
<p>Si c’est ce cas, alors je pense qu’il faut garder les spécificités des classes et faire une fonction générique qui les associe, du style</p>
<div class="hljs-code-div hljs-code-cpp"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span><span data-count="6"></span><span data-count="7"></span><span data-count="8"></span><span data-count="9"></span><span data-count="10"></span><span data-count="11"></span><span data-count="12"></span><span data-count="13"></span><span data-count="14"></span><span data-count="15"></span><span data-count="16"></span><span data-count="17"></span><span data-count="18"></span><span data-count="19"></span><span data-count="20"></span><span data-count="21"></span><span data-count="22"></span><span data-count="23"></span><span data-count="24"></span><span data-count="25"></span><span data-count="26"></span><span data-count="27"></span><span data-count="28"></span><span data-count="29"></span><span data-count="30"></span><span data-count="31"></span><span data-count="32"></span><span data-count="33"></span><span data-count="34"></span><span data-count="35"></span><span data-count="36"></span><span data-count="37"></span><span data-count="38"></span><span data-count="39"></span><span data-count="40"></span><span data-count="41"></span><span data-count="42"></span><span data-count="43"></span><span data-count="44"></span><span data-count="45"></span><span data-count="46"></span><span data-count="47"></span><span data-count="48"></span><span data-count="49"></span><span data-count="50"></span><span data-count="51"></span><span data-count="52"></span><span data-count="53"></span><span data-count="54"></span><span data-count="55"></span><span data-count="56"></span><span data-count="57"></span><span data-count="58"></span></div><pre><code class="hljs language-cpp"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MatA</span>{</span>
[...]
};
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MatB</span>{</span>
[...]
};
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SolvA</span>{</span>
<span class="hljs-keyword">public</span>:
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">factorize</span><span class="hljs-params">(<span class="hljs-keyword">const</span> MatA & mat)</span></span>; <span class="hljs-comment">// n'est capable de traiter que des MatA</span>
[...]
};
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SolvB</span>{</span>
<span class="hljs-keyword">public</span>:
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">factorize</span><span class="hljs-params">(<span class="hljs-keyword">const</span> MatB & mat)</span></span>; <span class="hljs-comment">// n'est capable de traiter que des MatB</span>
[...]
};
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SolvC</span>{</span>
<span class="hljs-keyword">public</span>:
<span class="hljs-comment">// pourquoi pas un solveur qui est capable de traiter les deux</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">factorize</span><span class="hljs-params">(<span class="hljs-keyword">const</span> MatA & mat)</span></span>;
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">factorize</span><span class="hljs-params">(<span class="hljs-keyword">const</span> MatB & mat)</span></span>;
[...]
};
<span class="hljs-function"><span class="hljs-keyword">template</span><class Matrice, class Solveur>
<span class="hljs-keyword">void</span> <span class="hljs-title">solve_system</span><span class="hljs-params">()</span>
</span>{
Matrice mat{...};
Solveur solv{...};
solv.<span class="hljs-built_in">factorize</span>(mat);
[...] <span class="hljs-comment">// le code qu'on avait dans le switch tout à l'heure</span>
}
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">traiter_config</span><span class="hljs-params">(Config config)</span>
</span>{
<span class="hljs-built_in"><span class="hljs-keyword">switch</span></span>(config)
{
<span class="hljs-keyword">case</span> configA:
solve_system<MatA, SolvA>();
<span class="hljs-keyword">break</span>;
<span class="hljs-keyword">case</span> configB:
solve_system<MatB, SolvB>();
<span class="hljs-keyword">break</span>;
<span class="hljs-keyword">case</span> configC:
solve_system<MatA, SolvC>();
<span class="hljs-keyword">break</span>;
<span class="hljs-keyword">case</span> configD:
solve_system<MatB, SolvC>();
<span class="hljs-keyword">break</span>;
<span class="hljs-keyword">case</span> configE:
solve_system<MatB, SolvA>(); <span class="hljs-comment">// ======> ERREUR à la compilation</span>
<span class="hljs-keyword">break</span>;
}
</code></pre></div>
<p>EDIT : je n’avais pas lu le dernier message, je comprends ce qui t’as conduit à mettre du template par dessus ton héritage et je pense effectivement que c’était une bonne idée, mais je ne suis pas tout à fait convaincu que l’héritage est utile.</p>[C++] Factory d'une classe qui spécialise une classe mère template, message #2366242021-08-13T11:32:43+02:00cvanaret/@cvanarethttps://zestedesavoir.com/forums/sujet/15589/c-factory-dune-classe-qui-specialise-une-classe-mere-template/?page=1#p236624<p>Je lis le choix de solveur et le choix de Hessienne depuis le fichier de config, donc tout se fait à runtime.</p>
<p><code>COOMatrix</code> et <code>CSCMatrix</code> héritent de <code>Matrix</code>.</p>
<p>Les solveurs sont des codes externes (en C ou Fortran) et j’implémente une interface par solveur (<code>MA57LinearSolver</code>, etc.). La classe mère <code>LinearSolver</code> ressemble à ça :</p>
<div class="hljs-code-div hljs-code-cpp"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span></div><pre><code class="hljs language-cpp"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LinearSolver</span> {</span>
<span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-keyword">void</span> <span class="hljs-title">factorize</span><span class="hljs-params">(<span class="hljs-keyword">const</span> SomeMatrixFormat& matrix)</span> </span>= <span class="hljs-number">0</span>;
<span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-keyword">void</span> <span class="hljs-title">solve</span><span class="hljs-params">(<span class="hljs-keyword">const</span> SomeMatrixFormat& matrix, <span class="hljs-keyword">const</span> std::vector<<span class="hljs-keyword">double</span>>& rhs)</span> </span>= <span class="hljs-number">0</span>;
}
</code></pre></div>
<p>Evidemment, je n’ai pas envie que tous les formats de matrice apparaissent, puisqu’en pratique, un solveur ne travaille que sur un format. Donc pour éviter ça :</p>
<div class="hljs-code-div hljs-code-cpp"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span><span data-count="6"></span></div><pre><code class="hljs language-cpp"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LinearSolver</span> {</span>
<span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-keyword">void</span> <span class="hljs-title">factorize</span><span class="hljs-params">(<span class="hljs-keyword">const</span> COOMatrix& matrix)</span> </span>= <span class="hljs-number">0</span>;
<span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-keyword">void</span> <span class="hljs-title">factorize</span><span class="hljs-params">(<span class="hljs-keyword">const</span> CSCMatrix& matrix)</span> </span>= <span class="hljs-number">0</span>;
<span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-keyword">void</span> <span class="hljs-title">solve</span><span class="hljs-params">(<span class="hljs-keyword">const</span> COOMatrix& matrix, <span class="hljs-keyword">const</span> std::vector<<span class="hljs-keyword">double</span>>& rhs)</span> </span>= <span class="hljs-number">0</span>;
<span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-keyword">void</span> <span class="hljs-title">solve</span><span class="hljs-params">(<span class="hljs-keyword">const</span> CSCMatrix& matrix, <span class="hljs-keyword">const</span> std::vector<<span class="hljs-keyword">double</span>>& rhs)</span> </span>= <span class="hljs-number">0</span>;
}
</code></pre></div>
<p>ou des signatures sur la classe mère (ce qui oblige à faire des casts dans les méthodes) :</p>
<div class="hljs-code-div hljs-code-cpp"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span></div><pre><code class="hljs language-cpp"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LinearSolver</span> {</span>
<span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-keyword">void</span> <span class="hljs-title">factorize</span><span class="hljs-params">(<span class="hljs-keyword">const</span> Matrix& matrix)</span> </span>= <span class="hljs-number">0</span>;
<span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-keyword">void</span> <span class="hljs-title">solve</span><span class="hljs-params">(<span class="hljs-keyword">const</span> Matrix& matrix, <span class="hljs-keyword">const</span> std::vector<<span class="hljs-keyword">double</span>>& rhs)</span> </span>= <span class="hljs-number">0</span>;
}
</code></pre></div>
<p>je template la classe :</p>
<div class="hljs-code-div hljs-code-cpp"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span></div><pre><code class="hljs language-cpp"><span class="hljs-keyword">template</span> <<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MatrixFormat</span>></span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LinearSolver</span> {</span>
<span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-keyword">void</span> <span class="hljs-title">factorize</span><span class="hljs-params">(<span class="hljs-keyword">const</span> MatrixFormat& matrix)</span> </span>= <span class="hljs-number">0</span>;
<span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-keyword">void</span> <span class="hljs-title">solve</span><span class="hljs-params">(<span class="hljs-keyword">const</span> MatrixFormat& matrix, <span class="hljs-keyword">const</span> std::vector<<span class="hljs-keyword">double</span>>& rhs)</span> </span>= <span class="hljs-number">0</span>;
}
</code></pre></div>
<p>Mes classes <code>MA57Solver</code> et <code>PardisoSolver</code> héritent de <code>LinearSolver</code> et spécialisent le paramètre du template avec le format de matrice qu’ils utilisent :</p>
<div class="hljs-code-div hljs-code-cpp"><div class="hljs-line-numbers"><span data-count="1"></span></div><pre><code class="hljs language-cpp"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MA57Solver</span>:</span> <span class="hljs-keyword">public</span> LinearSolver<COOMatrix>
</code></pre></div>
<p>ce qui me permet de définir les méthodes <code>factorize</code> et <code>solve</code> directement sur le bon format de matrice :</p>
<div class="hljs-code-div hljs-code-cpp"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span></div><pre><code class="hljs language-cpp"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MA57Solver</span>:</span> <span class="hljs-keyword">public</span> LinearSolver<COOMatrix> {
<span class="hljs-comment">// allocation de trucs, appels à Fortran</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">factorize</span><span class="hljs-params">(<span class="hljs-keyword">const</span> COOMatrix& matrix)</span> <span class="hljs-keyword">override</span></span>;
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">solve</span><span class="hljs-params">(<span class="hljs-keyword">const</span> COOMatrix& matrix, <span class="hljs-keyword">const</span> std::vector<<span class="hljs-keyword">double</span>>& rhs)</span> <span class="hljs-keyword">override</span></span>;
}
</code></pre></div>
<p>Idem pour <code>HessianEvaluation</code> : j’ai une classe mère abstraite, et autant de classes filles que de stratégies (exact, convexifiée, quasi-Newton). Cette classe stocke une Hessienne (une matrice creuse) qu’elle évalue et stocke directement dans le bon format. Donc j’ai templaté la classe mère et les classes filles :</p>
<div class="hljs-code-div hljs-code-cpp"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span><span data-count="6"></span><span data-count="7"></span><span data-count="8"></span><span data-count="9"></span><span data-count="10"></span></div><pre><code class="hljs language-cpp"><span class="hljs-keyword">template</span> <<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MatrixFormat</span>></span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HessianEvaluation</span> {</span>
MatrixFormat hessian;
}
<span class="hljs-keyword">template</span> <<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MatrixFormat</span>></span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ExactHessianEvaluation</span> :</span> <span class="hljs-keyword">public</span> HessianEvaluation<MatrixFormat> {
...
}
...
</code></pre></div>
<p>Ma classe <code>InteriorPointMethod</code> contient des <code>unique_ptr</code> vers le <code>LinearSolver</code> et la <code>HessianEvaluation</code>, donc je continue de templater :</p>
<div class="hljs-code-div hljs-code-cpp"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span><span data-count="6"></span><span data-count="7"></span><span data-count="8"></span><span data-count="9"></span><span data-count="10"></span><span data-count="11"></span><span data-count="12"></span><span data-count="13"></span><span data-count="14"></span><span data-count="15"></span><span data-count="16"></span><span data-count="17"></span><span data-count="18"></span><span data-count="19"></span></div><pre><code class="hljs language-cpp"><span class="hljs-keyword">template</span> <<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MatrixFormat</span>></span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">InteriorPointMethod</span> {</span>
<span class="hljs-keyword">public</span>:
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">compute_direction</span><span class="hljs-params">(...)</span> </span>{
<span class="hljs-comment">// evaluate the Hessian at the current point</span>
<span class="hljs-keyword">this</span>->hessian_evaluation-><span class="hljs-built_in">evaluate_hessian</span>(...);
<span class="hljs-comment">// form the linear system around this->hessian_evaluation.get_hessian()</span>
MatrixFormat linear_system = ...
<span class="hljs-comment">// compute symbolic factorization of the linear system</span>
<span class="hljs-keyword">this</span>->linear_solver-><span class="hljs-built_in">factorize</span>(linear_system);
...
}
<span class="hljs-keyword">private</span>:
std::unique_ptr<LinearSolver<MatrixFormat> > linear_solver;
std::unique_ptr<HessianEvaluation<MatrixFormat> > hessian_evaluation;
}
</code></pre></div>
<p>Au niveau des combinaisons possibles : <code>InteriorPointMethod</code> doit pouvoir tourner sur le produit cartésiens des stratégies :<br>
<strong>MA57 + Hessienne exacte, MA57 + Hessienne convexifiée, MA57 + Hessienne quasi-Newton</strong> (tout dans le format COO)<br>
<strong>Pardiso + Hessienne exacte, Pardiso + Hessienne convexifiée, Pardiso + Hessienne quasi-Newton</strong> (tout dans le format CSC). </p>
<p>Je suis conscient que c’est bizarre de tout templater alors que tout se fait au runtime <img src="/static/smileys/svg/rire.svg" alt=":lol:" class="smiley"> Ceci dit, si le paramètre du template a 2 valeurs possibles, ça se gère très bien à la volée dans une factory.</p>
<p>J’espère que le design est plus clair <img src="/static/smileys/svg/clin.svg" alt=";)" class="smiley"></p>[C++] Factory d'une classe qui spécialise une classe mère template, message #2366232021-08-13T10:50:50+02:00ads00/@ads00https://zestedesavoir.com/forums/sujet/15589/c-factory-dune-classe-qui-specialise-une-classe-mere-template/?page=1#p236623<p>Ca ne répond toujours pas aux questions <img src="/static/smileys/svg/langue.svg" alt=":p" class="smiley"></p>
<p>Il faudrait commencer par savoir ce que tu fais runtime et compile time, pourquoi tu as besoin d’avoir des objets pour tes solvers, quel est le code commun etc .. Tu dis que t’as un héritage pour tes solvers, mais qu’ils sont externes. On peut choisir entre 3 types de HessianEvaluation, mais tu mets un paramètre template lié au format de matrice.</p>
<p>Si tu fais une classe template mais que tu spécialises tous les types, autant faire des surcharges ou des classes nommées.</p>
<p>Ecris d’abord l’interface que tu veux pour différents cas, et ensuite tu fais ton design à partir de la. La tu pars du principe que tu veux X qui hérite de Y, et Z qui est templaté, mais on ne sait pas pourquoi.</p>[C++] Factory d'une classe qui spécialise une classe mère template, message #2366212021-08-13T10:04:13+02:00cvanaret/@cvanarethttps://zestedesavoir.com/forums/sujet/15589/c-factory-dune-classe-qui-specialise-une-classe-mere-template/?page=1#p236621<p>Merci pour vos réponses ! J’essaie effectivement de faire un truc compliqué, je vais récapituler ce qu’il se passe dans mon framework : en gros, </p>
<ul>
<li>j’ai une classe <code>InteriorPointMethod</code> qui résout des systèmes linéaires ;</li>
<li>le système linéaire est formé autour de la Hessienne du problème (matrice des dérivées secondes) au point courant. Il est représenté sous forme de matrice creuse ;</li>
<li>la Hessienne est évaluée par un membre de <code>InteriorPointMethod</code> de type <code>HessianEvaluation</code>. On peut choisir entre Hessienne exact, convexifiée et quasi-Newton ;</li>
<li>il existe plusieurs formats pour représenter des matrices creuses. J’en ai (au moins) deux (<a href="https://en.wikipedia.org/wiki/Sparse_matrix#Coordinate_list_(COO)">COO</a> et <a href="https://en.wikipedia.org/wiki/Sparse_matrix#Compressed_sparse_column_(CSC_or_CCS)">CSC/R</a>) ;</li>
<li>le système linéaire est résolu par des codes (solveurs) externes (par exemple <a href="https://www.hsl.rl.ac.uk/catalogue/ma57.html">MA57</a> ou <a href="https://www.pardiso-project.org/">PARDISO</a>) pour lesquels j’ai écrit des interfaces <code>MA57Solver</code> et <code>PardisoSolver</code> qui héritent de <code>LinearSolver</code> ;</li>
<li>ces solveurs linéaires travaillent chacun avec un seul format de matrice creuse : COO pour MA57 et CSC/R pour PARDISO ;</li>
<li>le choix d’un solveur linéaire (MA57 ou PARDISO) détermine donc le format de matrice creuse qui va être utilisé dans la séquence suivante : évaluation de la Hessienne, formation du système linéaire, résolution du système linéaire par le solveur linéaire ;</li>
<li>le solveur linéaire est choisi à l’exécution lorsque le fichier de configuration du framework est lu.</li>
</ul>
<p>J’ai donc templaté :</p>
<ul>
<li>ma classe <code>LinearSolver</code> afin de spécifier quel format de matrice le solveur peut résoudre (cf premier code du premier message) ;</li>
<li>la classe <code>HessianEvaluation</code> qui crée la matrice Hessienne directement dans le bon format creux ;</li>
<li>la classe <code>InteriorPointMethod</code> qui contient comme membres une instance de <code>LinearSolver</code> et une instance de <code>HessianEvaluation</code>.</li>
</ul>
<p><code>LinearSolver<MatrixType></code>, <code>HessianEvaluation<MatrixType></code> et <code>InteriorPointMethod<MatrixType></code> doivent être créés avec le même paramètre de template, ce qui permet de "fixer un unique format de matrice creuse dans tout le code".
J’ai donc dû templater mes factories (cf deuxième code du premier message) puisque les 3 classes <code>LinearSolver</code>, <code>HessianEvaluation</code> et <code>InteriorPointMethod</code> sont templatées. Je me retrouve donc avec ce drôle de code.</p>
<p>Ca vous inspire un autre design ?</p>[C++] Factory d'une classe qui spécialise une classe mère template, message #2365952021-08-12T15:28:22+02:00ads00/@ads00https://zestedesavoir.com/forums/sujet/15589/c-factory-dune-classe-qui-specialise-une-classe-mere-template/?page=1#p236595<p>Pour ton design, il faudrait que tu donnes plus d’infos, pourquoi tu veux utiliser de l’héritage, pourquoi tu as besoin d’un objet solver ? etc ..</p>
<p>Tu pourrais très bien avoir <code>linear_solver::machin(A)</code> et <code>linear_solver::truc(B)</code></p>
<p>Ça veut dire quoi "fixer le type des matrices" à l’exécution ?</p>[C++] Factory d'une classe qui spécialise une classe mère template, message #2365832021-08-12T11:26:22+02:00romantik/@romantikhttps://zestedesavoir.com/forums/sujet/15589/c-factory-dune-classe-qui-specialise-une-classe-mere-template/?page=1#p236583<p>Salut,<br>
Tu te fais des noeuds au cerveau, tu mélanges 2 approches pour résoudre le même problème de généricité.<br>
Tu n’as pas besoin du template pour redéfinir une méthode virtuelle, ou tu n’as pas besoin d’un héritage pour spécialiser ton template. </p>
<p>Quant aux unique_ptr qui ne comprennent pas l’affectation d’un unique_ptr d’une classe dérivée … c’est un peu dommage que ça ne marche pas, mais effectivement, à cause du template ce sont deux types distincts et donc ce n’est pas possible. En revanche il est possible de construire un "unique_ptr<Base>" autour d’un pointeur "Derive*"</p>[C++] Factory d'une classe qui spécialise une classe mère template, message #2365802021-08-12T11:01:33+02:00cvanaret/@cvanarethttps://zestedesavoir.com/forums/sujet/15589/c-factory-dune-classe-qui-specialise-une-classe-mere-template/?page=1#p236580<p>Voilà la solution que j’ai reçue sur StackOverflow : il faut spécialiser la factory.</p>
<div class="hljs-code-div hljs-code-cpp"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span><span data-count="6"></span><span data-count="7"></span><span data-count="8"></span><span data-count="9"></span><span data-count="10"></span><span data-count="11"></span><span data-count="12"></span><span data-count="13"></span><span data-count="14"></span><span data-count="15"></span><span data-count="16"></span></div><pre><code class="hljs language-cpp"><span class="hljs-keyword">template</span> <<span class="hljs-keyword">typename</span> MatrixType>
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Factory</span>;</span>
<span class="hljs-keyword">template</span> <>
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Factory</span><</span>MatrixTypeA> {
std::unique_ptr<LinearSolver<MatrixTypeA> > <span class="hljs-built_in">create</span>(<span class="hljs-keyword">const</span> std::string& name) {
<span class="hljs-comment">// select a LinearSolve<MatrixTypeA> and return it</span>
}
};
<span class="hljs-keyword">template</span> <>
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Factory</span><</span>MatrixTypeB> {
std::unique_ptr<LinearSolver<MatrixTypeB> > <span class="hljs-built_in">create</span>(<span class="hljs-keyword">const</span> std::string& name) {
<span class="hljs-comment">// select a LinearSolver<MatrixTypeB> and return it</span>
}
};
</code></pre></div>[C++] Factory d'une classe qui spécialise une classe mère template, message #2365702021-08-12T01:12:22+02:00cvanaret/@cvanarethttps://zestedesavoir.com/forums/sujet/15589/c-factory-dune-classe-qui-specialise-une-classe-mere-template/?page=1#p236570<p>Salut,<br>
je développe un framework C++ pour l’optimisation mathématique et je m’arrache un peu les cheveux pour trouver un design qui tient la route au niveau des matrices creuses.</p>
<p>En gros :</p>
<ul>
<li>j’ai deux types de représentations de matrices creuses : appelons les <code>A</code> et <code>B</code> ;</li>
<li>j’ai deux solveurs linéaires, <code>machin</code> et <code>truc</code>. <code>machin</code> travaille exclusivement avec des matrices de type <code>A</code> et <code>truc</code> exclusivement avec des matrices de type <code>B</code>.</li>
<li>du coup, j’aimerais templater mon code pour que le choix d’un solveur <code>machin</code> ou <code>truc</code> (à l’exécution) fixe le type des toutes les matrices (<code>A</code> ou <code>B</code>) dans tout le reste du code.</li>
</ul>
<p>J’ai essayé le code (simplifié) suivant :</p>
<div class="hljs-code-div hljs-code-cpp"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span><span data-count="6"></span><span data-count="7"></span><span data-count="8"></span><span data-count="9"></span><span data-count="10"></span><span data-count="11"></span><span data-count="12"></span></div><pre><code class="hljs language-cpp"><span class="hljs-keyword">template</span> <<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MatrixType</span>></span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LinearSolver</span> {</span>
<span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-keyword">void</span> <span class="hljs-title">factorize</span><span class="hljs-params">(<span class="hljs-keyword">const</span> MatrixType& matrix)</span> </span>= <span class="hljs-number">0</span>;
}
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LinearSolverMachin</span>:</span> <span class="hljs-keyword">public</span> LinearSolver<MatrixTypeA> {
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">factorize</span><span class="hljs-params">(<span class="hljs-keyword">const</span> MatrixTypeA& matrix)</span> <span class="hljs-keyword">override</span></span>;
}
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LinearSolverTruc</span>:</span> <span class="hljs-keyword">public</span> LinearSolver<MatrixTypeB> {
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">factorize</span><span class="hljs-params">(<span class="hljs-keyword">const</span> MatrixTypeB& matrix)</span> <span class="hljs-keyword">override</span></span>;
}
</code></pre></div>
<p>Le supertype commun à <code>LinearSolverMachin</code> et <code>LinearSolverTruc</code> étant le type template <code>LinearSolver</code>, j’ai templaté la classe <code>LinearSolverFactory</code> elle-même :</p>
<div class="hljs-code-div hljs-code-cpp"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span><span data-count="6"></span><span data-count="7"></span><span data-count="8"></span><span data-count="9"></span><span data-count="10"></span><span data-count="11"></span></div><pre><code class="hljs language-cpp"><span class="hljs-keyword">template</span><<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MatrixType</span>></span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LinearSolverFactory</span> {</span>
<span class="hljs-keyword">public</span>:
std::unique_ptr<LinearSolver<MatrixType> > <span class="hljs-built_in">create</span>(<span class="hljs-keyword">const</span> std::string& solver_name) {
<span class="hljs-keyword">if</span> (solver_name == <span class="hljs-string">"machin"</span>) {
<span class="hljs-keyword">return</span> std::make_unique<LinearSolverMachin>();
}
<span class="hljs-keyword">else</span> {
<span class="hljs-keyword">return</span> std::make_unique<LinearSolverTruc>();
}
}
</code></pre></div>
<p>Puis à plus haut niveau, j’appelle <code>LinearSolverFactory::create()</code> avec le bon nom de solveur et le bon paramètre de template.</p>
<p>Malheureusement le code ne compile pas (il me dit que le <code>std::unique_ptr<LinearSolverMachin></code> ne peut pas être converti en <code>std::unique_ptr<LinearSolver<MatrixTypeA> ></code>). Je ne suis pas assez calé en templates pour voir si le problème peut être corrigé. </p>
<p>Merci d’avance pour votre aide <img src="/static/smileys/svg/smile.svg" alt=":)" class="smiley"></p>
<p>Charlie</p>overloading fonctions template, message #2364772021-08-07T20:17:15+02:00mechap/@mechaphttps://zestedesavoir.com/forums/sujet/15576/overloading-fonctions-template/?page=1#p236477<p>Mais alors pourquoi gcc n’arrive pas a appeler le premier overload dans un contexte avec template ?</p>overloading fonctions template, message #2364762021-08-07T18:19:00+02:00ads00/@ads00https://zestedesavoir.com/forums/sujet/15576/overloading-fonctions-template/?page=1#p236476<p>C’est juste un bug à cause de la lambda.</p>
<div class="hljs-code-div hljs-code-cpp"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span></div><pre><code class="hljs language-cpp"><span class="hljs-keyword">auto</span> l = []{ <span class="hljs-keyword">return</span> []{}; };
<span class="hljs-keyword">template</span> <<span class="hljs-keyword">auto</span> = <span class="hljs-built_in">l</span>()>
</code></pre></div>
<p>Le 1er overload est pas aussi pertinent, il est plus spécialisé.</p>overloading fonctions template, message #2364732021-08-07T17:17:51+02:00mechap/@mechaphttps://zestedesavoir.com/forums/sujet/15576/overloading-fonctions-template/?page=1#p236473<p>Selon <a href="http://eel.is/c++draft/over.match.best">http://eel.is/c++draft/over.match.best</a>, je pense que l’appel <code>f</code> sans specifier d’arguments (de fonctions ou de template) devrait lever une erreur de compilation pour ambiguïté, puisque le premier overload est tout aussi pertinent que le second.</p>overloading fonctions template, message #2364722021-08-07T13:02:04+02:00Berdes/@Berdeshttps://zestedesavoir.com/forums/sujet/15576/overloading-fonctions-template/?page=1#p236472<p>En testant un peu:</p>
<ul>
<li>Clang 10 et 11 donnent une erreur pour <code>func1</code> et <code>func2</code></li>
<li>Clang 12 ne donne pas d’erreur</li>
<li>gcc >=10 donnent une erreur pour <code>func1</code> uniquement</li>
</ul>
<p>En regardant les règles de <a href="https://en.cppreference.com/w/cpp/language/function_template#Function_template_overloading">Function template overloading</a>, je ne vois rien qui indique que les règles de résolution pourraient être différente pour l’appel à <code>f</code> entre <code>func1</code> et <code>func2</code>. À partir de là, je dirais que gcc a probablement un bug.</p>overloading fonctions template, message #2364682021-08-06T16:18:24+02:00mechap/@mechaphttps://zestedesavoir.com/forums/sujet/15576/overloading-fonctions-template/?page=1#p236468<div class="custom-block custom-block-information"><div class="custom-block-body"><p>Le code ne fonctionne que sur gcc</p></div></div>
<div class="hljs-code-div hljs-code-cpp"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span><span data-count="6"></span><span data-count="7"></span><span data-count="8"></span><span data-count="9"></span><span data-count="10"></span><span data-count="11"></span><span data-count="12"></span><span data-count="13"></span><span data-count="14"></span><span data-count="15"></span></div><pre><code class="hljs language-cpp"><span class="hljs-keyword">template</span> <<span class="hljs-keyword">auto</span> = []{}>
<span class="hljs-keyword">consteval</span> <span class="hljs-keyword">auto</span> <span class="hljs-built_in">f</span>() { <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>; }
<span class="hljs-keyword">template</span> <<span class="hljs-keyword">typename</span> ...Args>
<span class="hljs-function"><span class="hljs-keyword">consteval</span> <span class="hljs-keyword">auto</span> <span class="hljs-title">f</span><span class="hljs-params">(Args &&...)</span> </span>{ <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>; }
<span class="hljs-keyword">template</span> <<span class="hljs-keyword">typename</span> T>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">func1</span><span class="hljs-params">()</span> </span>{
<span class="hljs-built_in"><span class="hljs-keyword">static_assert</span></span>(<span class="hljs-built_in">f</span>() == <span class="hljs-literal">false</span>); <span class="hljs-comment">// fails</span>
}
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">func2</span><span class="hljs-params">()</span> </span>{
<span class="hljs-built_in"><span class="hljs-keyword">static_assert</span></span>(<span class="hljs-built_in">f</span>() == <span class="hljs-literal">false</span>);
}
</code></pre></div>
<p>Pourquoi le premier overload de <code>f()</code> n’est jamais considere dans un contexte avec template (fonctions/structs/variables/etc templated) ? </p>conversion implicite pour une classe template dans une comparaison surchargé, message #2306052021-01-22T12:37:42+01:00lmghs/@lmghshttps://zestedesavoir.com/forums/sujet/14972/conversion-implicite-pour-une-classe-template-dans-une-comparaison-surcharge/?page=1#p230605<p>Au temps pour moi. Je dirais que l’on a des ambiguïtés maintenant. Il faut employer quel opérateur pour comparer maintenant? Celui qui passe vers du N ou celui vers du M?</p>
<p>Du coup, je ne vois guère de solution sans un opérateur qui prend T, M et N et s’occupe lui même de gérer les mixités.</p>conversion implicite pour une classe template dans une comparaison surchargé, message #2306032021-01-22T11:05:12+01:00d3m0t3p/@d3m0t3phttps://zestedesavoir.com/forums/sujet/14972/conversion-implicite-pour-une-classe-template-dans-une-comparaison-surcharge/?page=1#p230603<p>Le constructeur de conversion est <code>Unsigned(const Unsigned<T,M>& other) : Unsigned<T,N>() { /*code*/}</code> qui prend un type M en paramètre alors que la classe est de type N. Avec ça lorsque j’ai <code>Unsigned<T,N> < Unsigned <T,M></code> un objet de type <code><T,N></code> est créé en utilisant le constructeur ci-dessus. Et je compare donc un <code>T,N</code> avec un <code>T,N</code>. Et j’ai définis l’opérateur de comparaison pour ces types.</p>
<p>Pour ce qui est du const sur les amis, j’ai raté mon copier coller, c’est corrigé</p>conversion implicite pour une classe template dans une comparaison surchargé, message #2305982021-01-22T02:01:21+01:00lmghs/@lmghshttps://zestedesavoir.com/forums/sujet/14972/conversion-implicite-pour-une-classe-template-dans-une-comparaison-surcharge/?page=1#p230598<p>Le const dans la version amie ne devrait pas compiler. Cela n’a pas de sens.</p>
<p>Sinon, il me semblait qu’il y avait un vieille règle qui interdisait les conversion implicites en cas de template, mais non… ça compile: <a href="https://godbolt.org/z/4eGe6n">https://godbolt.org/z/4eGe6n</a>
Bizarre :/</p>
<p>Bref. L’écriture idéale jusqu’au C++17: les opérateurs amis définis inlines dans la définition de la classe histoire de les cacher. Et dans tes exemples, il manque le constructeur de conversion. </p>
<p>A partir du C++20, on définit le <em>spaceship operator</em>: <a href="https://godbolt.org/z/d5f56T">https://godbolt.org/z/d5f56T</a></p>