DOC: document some of the language limitations highlighted by this research
This commit is contained in:
parent
d8f7a22123
commit
134821ca15
4 changed files with 95 additions and 40 deletions
|
|
@ -222,6 +222,33 @@ Thus -- as of 9/2014 -- the _rules of the game_ are as folows
|
|||
|
||||
|
||||
|
||||
New Capabilities
|
||||
----------------
|
||||
As the support for the new language standard matures, we're able to extend our capabilities to write code
|
||||
more succinctly and clearer to read. Sometimes we have to be careful in order to use these new powers the right way...
|
||||
|
||||
Variadic Templates
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
Programming with variadic template arguments can be surprisingly tricky and the resulting code design tends to be confusing.
|
||||
This is caused by the fact that a _»variadic argument pack«_ is not a type proper. Rather, it is a syntactic (not lexical)
|
||||
macro. It is thus not possible to ``grab'' the type from variadic arguments and pass it directly to some metaprogramming
|
||||
helper or metafunction. The only way to dissect and evaluate such types is kind of _going backwards_: pass them to some
|
||||
helper function or template with explicit specialisations, thereby performing a pattern match.
|
||||
|
||||
A typical example: Let's assume we're about to write a function or constructor to accept flexible arguments, but we
|
||||
want to ensure those arguments to conform to some standards. Maybe we can handle only a small selection of types
|
||||
sensibly within the given context -- and thus we want to check and produce a compilation failure otherwise.
|
||||
|
||||
Unfortunately, this is not possible, at least not directly. If we want to get at the argument types one by one, we need
|
||||
to fabricate some variadic template helper type, and then add an explicit specialisation to it, for the sole purpose of
|
||||
writing a pattern match `<Arg a, Args...>`. Another common technique is to fabricate a `std::tuple<ARGS...>` and then
|
||||
to use the `std::get<i> (std::tuple<ARGS...>)` to grab individual types. Such might be bewildering for the casual
|
||||
reader, since we never intend to create a storage tuple anywhere.
|
||||
|
||||
NOTE: we might consider to reshape our typelist to be a real `lib::meta::Types<TYPES...>` and outfit it with some
|
||||
of the most frequently needed accessors. At least the name ``Types'' is somewhat more indicative than ``tuple''.
|
||||
|
||||
|
||||
Type inference
|
||||
~~~~~~~~~~~~~~
|
||||
Generally speaking, the use of `auto` and type inference has the potential to significantly improve readability of
|
||||
|
|
@ -245,3 +272,19 @@ Forward Iterator concept. There are some notable pitfalls to consider, though
|
|||
when just passing the result from a delegate function (or lambda!).
|
||||
|
||||
|
||||
Generic Lambdas
|
||||
~~~~~~~~~~~~~~~
|
||||
A generic lambda (i.e. using `auto` on some argument) produces _a template_, not a regular `operator()` function.
|
||||
Whenever such a lambda is invoked, this template is instantiated. This has several ramifications. For one, the
|
||||
notorious techniques for detecting a function work well on conventional lambdas, but _fail on generic lambdas_.
|
||||
And it is not possible to ``probe'' a template with SFINAE: If you instantiate a template, and the result is not
|
||||
valid, you've produced a _compilation failure_, not a _substitution failure._ Which means, the compilation aborts,
|
||||
instead of just trying the next argument substitution (as you'd might expect). Another consequence is that you
|
||||
can not _just accept a generic lambda as argument and put it into a `std::function` (which otherwise is the
|
||||
idiomatic way of ``just accepting anything function-like''). In order to place a generic lambda into a `std::function`,
|
||||
you must decide upon the concrete argument and return type(s) and thus instantiate the template at that point.
|
||||
|
||||
The only remedy to get around these serious limitations will be when C++ starts to support *Concepts* -- which
|
||||
can be expected to replace the usage of unbounded type inference in many situations: for example, it would then
|
||||
be possible to accept just ``an Iterator'' or ``a STL container''...
|
||||
|
||||
|
|
|
|||
|
|
@ -266,6 +266,7 @@ namespace lib {
|
|||
{ };
|
||||
|
||||
|
||||
/** decide how to adapt and embed the source sequence into the resulting TreeExplorer */
|
||||
template<class SRC, typename SEL=void>
|
||||
struct _TreeExplorerTraits
|
||||
{
|
||||
|
|
@ -602,7 +603,12 @@ namespace lib {
|
|||
|
||||
|
||||
/* ==== convenient builder free functions ==== */
|
||||
|
||||
|
||||
/** start building a TreeExplorer
|
||||
* by suitably wrapping the given iterable source.
|
||||
* @return a TreeEplorer, which is an Iterator to yield all the source elements,
|
||||
* but may also be used to build an processing pipeline.
|
||||
*/
|
||||
template<class IT>
|
||||
inline auto
|
||||
treeExplore (IT&& srcSeq)
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
** Similar to IterExplorer_test, the his test relies on a demonstration setup featuring
|
||||
** a custom encapsulated state type: we rely on a counter with start and end value,
|
||||
** embedded into an iterator. Basically, this running counter, when iterated, generates
|
||||
** a sequence of numbers start ... end.
|
||||
** a descending sequence of numbers start ... end.
|
||||
** So -- conceptually -- this counting iterator can be thought to represent this
|
||||
** sequence of numbers. Note that this is a kind of abstract or conceptual
|
||||
** representation, not a factual representation of the sequence in memory.
|
||||
|
|
@ -84,7 +84,7 @@ namespace test{
|
|||
|
||||
/**
|
||||
* This iteration _"state core" type_ describes
|
||||
* a sequence of numbers yet to be delivered.
|
||||
* a descending sequence of numbers yet to be delivered.
|
||||
*/
|
||||
class CountDown
|
||||
{
|
||||
|
|
@ -119,8 +119,9 @@ namespace test{
|
|||
|
||||
|
||||
/**
|
||||
* A straight ascending number sequence as basic test iterator.
|
||||
* The tests will dress up this source sequence in various ways.
|
||||
* A straight descending number sequence as basic test iterator.
|
||||
* It is built wrapping an opaque "state core" (of type CountDown).
|
||||
* @note the "state core" is not accessible from the outside
|
||||
*/
|
||||
class NumberSequence
|
||||
: public IterStateWrapper<uint, CountDown>
|
||||
|
|
@ -138,9 +139,7 @@ namespace test{
|
|||
|
||||
|
||||
|
||||
/** Diagnostic helper: "squeeze out" the given iterator
|
||||
* and join all the elements yielded into a string
|
||||
*/
|
||||
/** Diagnostic helper: join all the elements from a _copy_ of the iterator */
|
||||
template<class II>
|
||||
inline string
|
||||
materialise (II&& ii)
|
||||
|
|
@ -148,6 +147,7 @@ namespace test{
|
|||
return util::join (std::forward<II> (ii), "-");
|
||||
}
|
||||
|
||||
/** Diagnostic helper: "squeeze out" the given iterator until exhaustion */
|
||||
template<class II>
|
||||
inline void
|
||||
pullOut (II & ii)
|
||||
|
|
@ -183,17 +183,18 @@ namespace test{
|
|||
* ## Explanation
|
||||
*
|
||||
* Both this test and the IterExplorer template might be bewildering
|
||||
* and cryptic, unless you know the *Monad design pattern*. Monads are
|
||||
* heavily used in functional programming, actually they originate
|
||||
* from Category Theory. Basically, Monad is a pattern where we
|
||||
* combine several computation steps in a specific way; but instead
|
||||
* of intermingling the individual computation steps and their
|
||||
* combination, the goal is to isolate and separate the _mechanics
|
||||
* of combination_, so we can focus on the actual _computation steps:_
|
||||
* The mechanics of combination are embedded into the Monad type,
|
||||
* which acts as a kind of container, holding some entities
|
||||
* to be processed. The actual processing steps are then
|
||||
* fed to the monad as "function object" parameters.
|
||||
* and cryptic, unless you know the *Monad design pattern*. »Monads«
|
||||
* are heavily used in functional programming, actually they originate
|
||||
* from Category Theory. Basically, Monad is a pattern where we combine
|
||||
* several computation steps in a specific way; but instead of intermingling
|
||||
* the individual computation steps and their combination, the goal is to
|
||||
* isolate and separate the _mechanics of combination_, so we can focus on
|
||||
* the actual _computation steps:_ The mechanics of combination are embedded
|
||||
* into the Monad type, which acts as a kind of container, holding some entities
|
||||
* to be processed. The actual processing steps are then attached to the monad as
|
||||
* "function object" parameters. It is up to the monad to decide if, and when
|
||||
* those processing steps are applied to the embedded values and how to combine
|
||||
* the results into a new monad.
|
||||
*
|
||||
* Using the monad pattern is well suited when both the mechanics of
|
||||
* combination and the individual computation steps tend to be complex.
|
||||
|
|
@ -279,7 +280,7 @@ namespace test{
|
|||
CHECK (materialise(ii) == "-2-3--5-8--13");
|
||||
CHECK (materialise(jj) == "3--5-8--13");
|
||||
|
||||
// can adapt STL container automatically
|
||||
// can even adapt STL container automatically
|
||||
auto kk = treeExplore(numz);
|
||||
CHECK (!isnil (kk));
|
||||
CHECK (1 == *kk);
|
||||
|
|
@ -325,17 +326,19 @@ namespace test{
|
|||
void
|
||||
verify_expandOperation()
|
||||
{
|
||||
/* == "monadic flatMap" == */
|
||||
|
||||
verify_treeExpandingIterator(
|
||||
treeExplore(CountDown{5})
|
||||
.expand([](uint j){ return CountDown{j-1}; })
|
||||
.expand([](uint j){ return CountDown{j-1}; }) // expand-functor: Val > StateCore
|
||||
);
|
||||
|
||||
verify_treeExpandingIterator(
|
||||
treeExplore(CountDown{5})
|
||||
.expand([](uint j){ return NumberSequence{j-1}; })
|
||||
.expand([](uint j){ return NumberSequence{j-1}; }) // expand-functor: Val > Iter
|
||||
);
|
||||
|
||||
// lambda with side-effect and a different return type
|
||||
// lambda with side-effect and return type different from source iter
|
||||
vector<vector<uint>> childBuffer;
|
||||
auto expandIntoChildBuffer = [&](uint j) -> vector<uint>&
|
||||
{
|
||||
|
|
@ -348,35 +351,38 @@ namespace test{
|
|||
|
||||
verify_treeExpandingIterator(
|
||||
treeExplore(CountDown{5})
|
||||
.expand(expandIntoChildBuffer)
|
||||
.expand(expandIntoChildBuffer) // expand-functor: Val > STL-container&
|
||||
);
|
||||
|
||||
// test routine called the expansion functor five times
|
||||
CHECK (5 == childBuffer.size());
|
||||
|
||||
|
||||
|
||||
/* == "state manipulation" use cases == */
|
||||
|
||||
verify_treeExpandingIterator(
|
||||
treeExplore(CountDown{5})
|
||||
.expand([](CountDown const& core){ return CountDown{ yield(core) - 1}; })
|
||||
.expand([](CountDown const& core){ return CountDown{ yield(core) - 1}; }) // expand-functor: StateCore const& -> StateCore
|
||||
);
|
||||
|
||||
verify_treeExpandingIterator(
|
||||
treeExplore(CountDown{5})
|
||||
.expand([](CountDown const& core){ return NumberSequence{ yield(core) - 1}; })
|
||||
.expand([](CountDown core){ return NumberSequence{ yield(core) - 1}; }) // expand-functor: StateCore -> Iter
|
||||
);
|
||||
|
||||
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1118 : GDB Segfault on loading the inferior
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1118 : Generated code works just fine and passes Test though
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1118 : GDB Segfault on loading the inferior
|
||||
verify_treeExpandingIterator(
|
||||
treeExplore(CountDown{5})
|
||||
.expand([](auto & it){ return CountDown{ *it - 1}; })
|
||||
.expand([](auto & it){ return CountDown{ *it - 1}; }) // generic Lambda: Iter& -> StateCore
|
||||
);
|
||||
|
||||
verify_treeExpandingIterator(
|
||||
treeExplore(CountDown{5})
|
||||
.expand([](auto it){ return decltype(it){ *it - 1}; })
|
||||
.expand([](auto it){ return decltype(it){ *it - 1}; }) // generic Lambda: Iter -> Iter
|
||||
);
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1118 : GDB Segfault on loading the inferior
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -5100,8 +5100,8 @@
|
|||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1510941463562" ID="ID_845080696" MODIFIED="1510941523082" TEXT="TreeExplorer">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1511571446467" ID="ID_1778455053" MODIFIED="1511571478593" TEXT="expand-Operation">
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node COLOR="#338800" CREATED="1511571446467" ID="ID_1778455053" MODIFIED="1511757459789" TEXT="expand-Operation">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#338800" CREATED="1511227813712" ID="ID_1961708112" MODIFIED="1511228177906" TEXT="Fall-1">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1511227821087" ID="ID_143800006" MODIFIED="1511227823932" TEXT="monadisch"/>
|
||||
|
|
@ -5163,7 +5163,7 @@
|
|||
<node CREATED="1511402855174" ID="ID_386377958" MODIFIED="1511402861001" TEXT="beides können unterschiedliche Typen sein"/>
|
||||
<node CREATED="1511402861613" ID="ID_1536434222" MODIFIED="1511402872072" TEXT="solange der expandFunktor beide akzeptiert...."/>
|
||||
</node>
|
||||
<node CREATED="1511455454190" ID="ID_1384978479" MODIFIED="1511455481991" TEXT="Problem: kein generischer Expand-Functor">
|
||||
<node CREATED="1511455454190" FOLDED="true" ID="ID_1384978479" MODIFIED="1511757482192" TEXT="Problem: kein generischer Expand-Functor">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
<node CREATED="1511455484027" ID="ID_548359395" MODIFIED="1511572072779" TEXT="unmittelbare Folge davon, daß wir den Typ des Expand-Funktors analysieren">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
|
|
@ -5313,17 +5313,17 @@
|
|||
<node COLOR="#338800" CREATED="1511578319810" ID="ID_1918720180" MODIFIED="1511578585730" TEXT="unterstützte Varianten dokumentieren">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1511578330448" ID="ID_1808038526" MODIFIED="1511578396263" TEXT="alle unterstützten Varianten im Test abdecken">
|
||||
<arrowlink COLOR="#b65d79" DESTINATION="ID_1298407683" ENDARROW="Default" ENDINCLINATION="-33;-51;" ID="Arrow_ID_1214344498" STARTARROW="None" STARTINCLINATION="-292;0;"/>
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node CREATED="1511578330448" ID="ID_1808038526" MODIFIED="1511757433857" TEXT="alle unterstützten Varianten im Test abdecken">
|
||||
<arrowlink COLOR="#5d68b6" DESTINATION="ID_1298407683" ENDARROW="Default" ENDINCLINATION="-33;-51;" ID="Arrow_ID_1214344498" STARTARROW="None" STARTINCLINATION="-292;0;"/>
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1510941467586" ID="ID_381928532" MODIFIED="1510941523722" TEXT="TreeExplorer_test">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1510969031378" ID="ID_1678287752" MODIFIED="1510969040176" TEXT="verify_wrappedIterator();">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node COLOR="#338800" CREATED="1510969031378" ID="ID_1678287752" MODIFIED="1511757396655" TEXT="verify_wrappedIterator();">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1511054727547" ID="ID_328168347" MODIFIED="1511054858441" TEXT="Problem: muß Iterator-Arten unterscheiden">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
</node>
|
||||
|
|
@ -5343,9 +5343,9 @@
|
|||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1510969031379" ID="ID_1298407683" MODIFIED="1511578384759" TEXT="verify_expandOperation();">
|
||||
<linktarget COLOR="#b65d79" DESTINATION="ID_1298407683" ENDARROW="Default" ENDINCLINATION="-33;-51;" ID="Arrow_ID_1214344498" SOURCE="ID_1808038526" STARTARROW="None" STARTINCLINATION="-292;0;"/>
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node COLOR="#338800" CREATED="1510969031379" ID="ID_1298407683" MODIFIED="1511757399431" TEXT="verify_expandOperation();">
|
||||
<linktarget COLOR="#5d68b6" DESTINATION="ID_1298407683" ENDARROW="Default" ENDINCLINATION="-33;-51;" ID="Arrow_ID_1214344498" SOURCE="ID_1808038526" STARTARROW="None" STARTINCLINATION="-292;0;"/>
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#338800" CREATED="1511227813712" ID="ID_942570698" MODIFIED="1511228177906" TEXT="Fall-1">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1511227821087" ID="ID_1644135845" MODIFIED="1511571551953" TEXT="monadisch / funktional">
|
||||
|
|
|
|||
Loading…
Reference in a new issue