diff --git a/research/try.cpp b/research/try.cpp index 41554a164..f420dfa53 100644 --- a/research/try.cpp +++ b/research/try.cpp @@ -10,7 +10,7 @@ // 4/08 - comparison operators on shared_ptr // 4/08 - conversions on the value_type used for boost::any // 5/08 - how to guard a downcasting access, so it is compiled in only if the involved types are convertible -// 7/08 - combining partial specialisation and subclasses +// 7/08 - combining partial specialisation and subclasses // 10/8 - abusing the STL containers to hold noncopyable values // 6/09 - investigating how to build a mixin template providing an operator bool() // 12/9 - tracking down a strange "warning: type qualifiers ignored on function return type" @@ -35,29 +35,68 @@ // 9/17 - manipulate variadic templates to treat varargs in several chunks // 11/17 - metaprogramming to detect the presence of extension points // 11/17 - detect generic lambda -// 12/17 - investigate SFINAE failure +// 12/17 - investigate SFINAE failure. Reason was indirect use while in template instantiation /** @file try.cpp - ** Bug hunting: a typedef detecting metafunction fails under circumstances yet to be investigated. + ** Bug hunt: a typedef detecting metafunction failed mysteriously. + ** As it turned out, the implemented check required a _complete definition_, since it applied `sizeof()`. + ** But the Detection was invoked indirectly from a template while this was still in instantiation. + ** This explains why the metafunction worked in a clean test setup, but failed when integrated + ** in the actual code base. The tricky and insidious part of this story is the fact, that + ** in a regular definition, this would cause a compilation failure. But since our detector + ** relies on SFINAE, just the detection went wrong, and consequently an improper template + ** specialisation was picked. These are the perils of metaprogramming with a language + ** never really made for functional programming... + ** + ** The solution or workaround is simple: use a detection technique able to work with incomplete types. + ** + ** For context: lib::diff::Record defines various Iterators. And lib::dif::GenNode is a _recursive datatype_, + ** which means, already the definition of GenNode requires the instantiation of `Record`. Now we added + ** some detector magic to our Iterator Adapters, and this magic failed on the Iterators defined by `Record,` + ** because `Record` was not fully instantiated at that point. */ typedef unsigned int uint; -#include "lib/meta/value-type-binding.hpp" -#include "lib/diff/gen-node.hpp" #include "lib/format-cout.hpp" +#include "lib/meta/util.hpp" +#include "lib/util.hpp" + +#include -namespace lib { -namespace meta{ + template + struct Test_Incomplete + { }; -}} + template + struct Test_Complete + { }; -//using lib::meta::enable_if; -using ElmIter = lib::diff::RecordSetup::ElmIter; -using RecIter = lib::diff::Rec::iterator; +using lib::meta::Yes_t; +using lib::meta::No_t; + + template + static Yes_t detectComplete(Test_Complete * ); + template + static No_t detectComplete(...); + + template + static Yes_t detectIncomplete(Test_Incomplete * ); + template + static No_t detectIncomplete(...); + + + + struct InStatuNascendi + { + using Iter = std::vector::iterator; + + static const auto detectedComplete = (sizeof(Yes_t) == sizeof(detectComplete(0))); // will produce No, erroneously + static const auto detectedIncomplete = (sizeof(Yes_t) == sizeof(detectIncomplete(0))); // will produce Yes + }; #define SHOW_TYPE(_TY_) \ @@ -70,10 +109,8 @@ using RecIter = lib::diff::Rec::iterator; int main (int, char**) { - SHOW_TYPE (ElmIter); - SHOW_TYPE (RecIter); - - SHOW_TYPE (lib::meta::TypeBinding::pointer); + SHOW_EXPR (InStatuNascendi::detectedComplete); + SHOW_EXPR (InStatuNascendi::detectedIncomplete); cout << "\n.gulp.\n"; diff --git a/src/lib/iter-adapter-stl.hpp b/src/lib/iter-adapter-stl.hpp index ed04ca37c..e412973e8 100644 --- a/src/lib/iter-adapter-stl.hpp +++ b/src/lib/iter-adapter-stl.hpp @@ -44,6 +44,7 @@ #include "lib/iter-adapter.hpp" #include "lib/iter-adapter-ptr-deref.hpp" +#include namespace lib { diff --git a/src/lib/meta/duck-detector.hpp b/src/lib/meta/duck-detector.hpp index 436545c47..198bbabba 100644 --- a/src/lib/meta/duck-detector.hpp +++ b/src/lib/meta/duck-detector.hpp @@ -70,11 +70,19 @@ ** Effectively this means that an error in the test expression might go unnoticed; ** you'd be better off explicitly checking the detection result by an unit test. ** - ** There are several typical problems to care about + ** There are several *typical problems* to care about ** - a member can be both a variable or a function of that name ** - function signatures need to match precisely, including const modifiers ** - the generated metafunction (template) uses a type parameter 'TY', which could ** shadow or conflict with an type parameter in the enclosing scope + ** - some of the detectors _require a complete type_ to work properly. They create a + ** pointer-to-member or invoke `sizeof()`. In regular code, doing such on an incomplete + ** type would provoke a _compilation failure_ -- however, here this code gets evaluated + ** in a SFINAE context, which means, it will fail silently and thus produce a wrong + ** detection result. This can be quite *insidious* when relying on the proper detection + ** to pick the right implementation/specialisation; especially when instantiating + ** _mutually dependent_ templates, the distinction between "complete" and "incomplete" + ** can be rather arbitrary while in the process of instantiation. ** - the member and function checks rely on member pointers, which generally refer to ** the explicit static type. These checks won't see any inherited members / functions. ** - obviously, all those checks are never able to detect anything depending on runtime diff --git a/src/lib/meta/value-type-binding.hpp b/src/lib/meta/value-type-binding.hpp index 2e2084821..41a37563e 100644 --- a/src/lib/meta/value-type-binding.hpp +++ b/src/lib/meta/value-type-binding.hpp @@ -70,9 +70,9 @@ namespace meta { template class has_nested_ValueTypeBindings { - template struct Probe { }; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index f7388e22f..0567048f1 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -21692,6 +21692,99 @@ + + + + + + +

+ Lessons +

+

+ learned +

+ + +
+ + + + + + + + + + +

+ auf incomplete type achten +

+ + +
+ + + + + + + + +

+ Vorsicht bei +

+

+ mutually dependent templates +

+ + +
+ + + + + + +

+ kann eines der Templates im Zyklus vorrübergehend als "incomplete" gelten. +

+ + +
+
+ + + + + + +

+ ...wenn man dummerweise auf verschlungenen Pfaden +

+

+ genau in dieser Phase die Metafunktion anfragt, +

+

+ kann der betreffende Check stillschweigend scheitern. +

+

+ +

+

+ Konsequenz: man wählt dann z.B. eine subtil falsche Spezialisierung. +

+ + +
+
+ + + +
+
+
+