investigate insidious ill-guided conversion

As it turns out, using the functional-notation form conversion
with *parentheses* will fall back on a C-style (wild, re-interpret) cast
when the target type is *not* a class. As in the case in question here, where
it is a const& to a class. To the contrary, using *curly braces* will always
attempt to go through a constructor, and thus fail as expected, when there is
no conversion path available.

I wasn't aware of that pitfall. I noticed it since the recently introduced
class TimelineGui lacked a conversion operator to BareEntryID const& and just
happily used the TimelineGui object itself and did a reinterpret_cast into BareEntryID
This commit is contained in:
Fischlurch 2018-10-12 23:42:56 +02:00
parent e81b0592d3
commit fa6ba76f85
14 changed files with 92 additions and 69 deletions

View file

@ -39,11 +39,14 @@
// 03/18 - Dependency Injection / Singleton initialisation / double checked locking
// 04/18 - investigate construction of static template members
// 08/18 - Segfault when compiling some regular expressions for EventLog search
// 10/18 - investigate insidious reinterpret cast
/** @file try.cpp
* Heisenbug hunt: random crashes in BusTerm_test, seemingly emerging from regular expression compilation.
* Not able to reproduce the crash, unfortunately. //////////////////////////////////////////////////////////TICKET #1158
* Document an insidious wild cast, caused by the syntax `Type(arg)`.
* I was under the wrong assumption this would be handled equivalent to a constructor invocation.
* Seemingly it is rather handled as a C-style cast, i.e. equivalent to `(Type)arg`.
* @see [Question on Stackoverflow](https://stackoverflow.com/q/52782967/444796)
*/
typedef unsigned int uint;
@ -52,12 +55,10 @@ typedef unsigned int uint;
#include "lib/test/test-helper.hpp"
#include "lib/util.hpp"
#include <regex>
#include <string>
#include <vector>
using std::string;
using VecS = std::vector<string>;
using util::isSameObject;
@ -69,25 +70,32 @@ using VecS = std::vector<string>;
class Wau
{
int i = -1;
};
class Miau
{
public:
uint u = 1;
};
int
main (int, char**)
{
VecS rexs{{"after.+_ATTRIBS_.+ins.+1 of 62 ≺293.gen029≻.+mut.+1 of 62 ≺293.gen029≻.+ins.+borgID.+293.+emu.+1 of 62 ≺293.gen029≻"
,"after.+_ATTRIBS_.+ins.+1 of 64 ≺251.gen019≻.+mut.+1 of 64 ≺251.gen019≻.+ins.+borgID.+251.+emu.+1 of 64 ≺251.gen019≻"
,"after.+_ATTRIBS_.+ins.+1 of 8 ≺203.gen036≻.+mut.+1 of 8 ≺203.gen036≻.+ins.+borgID.+203.+emu.+1 of 8 ≺203.gen036≻"
,"after.+?_ATTRIBS_.+?ins.+?53 of 57 ≺358.gen010≻.+?mut.+?53 of 57 ≺358.gen010≻.+?ins.+?borgID.+?358.+?emu.+?53 of 57 ≺358.gen010≻"
,"after.+?_ATTRIBS_.+?ins.+?53 of 63 ≺178.gen028≻.+?mut.+?53 of 63 ≺178.gen028≻.+?ins.+?borgID.+?178.+?emu.+?53 of 63 ≺178.gen028≻"
,"after.+?_ATTRIBS_.+?ins.+?53 of 59 ≺498.gen038≻.+?mut.+?53 of 59 ≺498.gen038≻.+?ins.+?borgID.+?498.+?emu.+?53 of 59 ≺498.gen038≻"
,"after.+?_ATTRIBS_.+?ins.+?53 of 60 ≺223.gen003≻.+?mut.+?53 of 60 ≺223.gen003≻.+?ins.+?borgID.+?223.+?emu.+?53 of 60 ≺223.gen003≻"
,"after.+?_ATTRIBS_.+?ins.+?53 of 78 ≺121.gen015≻.+?mut.+?53 of 78 ≺121.gen015≻.+?ins.+?borgID.+?121.+?emu.+?53 of 78 ≺121.gen015≻"
}};
for (auto const& str : rexs)
{
auto rex = std::regex{str};
SHOW_EXPR (std::regex_search(str, rex));
}
Wau wau;
using ID = Miau &;
ID wuff = ID(wau);
cout << "Miau=" << wuff.u
<< " ref to same object: " << isSameObject (wau, wuff)
<< endl;
cout << "\n.gulp.\n";

View file

@ -140,7 +140,7 @@ namespace interact {
})
.matchElement ([&](GenNode const& spec, TimelineGui const& elm) -> bool
{ // »Matcher« : how to know we're dealing with the right timeline object
return spec.idi == ID(elm);
return spec.idi == ID{elm};
})
.constructFrom ([&](GenNode const& spec) -> TimelineGui
{ // »Constructor« : what to do when the diff mentions a new entity
@ -148,7 +148,7 @@ namespace interact {
})
.buildChildMutator ([&](TimelineGui& targetTimeline, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
{ // »Mutator« : how to apply the diff recursively to a nested scope
if (ID(targetTimeline) != subID) return false;
if (ID{targetTimeline} != subID) return false;
targetTimeline.buildMutator (buff); // - delegate to child(Timeline) to build nested TreeMutator
return true;
}))

View file

@ -99,7 +99,7 @@ namespace timeline {
})
.matchElement ([&](GenNode const& spec, PMarker const& elm) -> bool
{
return spec.idi == ID(elm);
return spec.idi == ID{*elm};
})
.constructFrom ([&](GenNode const& spec) -> PMarker
{
@ -107,7 +107,7 @@ namespace timeline {
})
.buildChildMutator ([&](PMarker& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
{
if (ID(target) != subID) return false;
if (ID{*target} != subID) return false;
target->buildMutator (buff);
return true;
}))
@ -118,7 +118,7 @@ namespace timeline {
})
.matchElement ([&](GenNode const& spec, PEffect const& elm) -> bool
{
return spec.idi == ID(elm);
return spec.idi == ID{*elm};
})
.constructFrom ([&](GenNode const& spec) -> PEffect
{
@ -126,7 +126,7 @@ namespace timeline {
})
.buildChildMutator ([&](PEffect& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
{
if (ID(target) != subID) return false;
if (ID{*target} != subID) return false;
target->buildMutator (buff);
return true;
}))
@ -137,7 +137,7 @@ namespace timeline {
})
.matchElement ([&](GenNode const& spec, PChannel const& elm) -> bool
{
return spec.idi == ID(elm);
return spec.idi == ID{*elm};
})
.constructFrom ([&](GenNode const& spec) -> PChannel
{
@ -145,7 +145,7 @@ namespace timeline {
})
.buildChildMutator ([&](PChannel& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
{
if (ID(target) != subID) return false;
if (ID{*target} != subID) return false;
target->buildMutator (buff);
return true;
})));

View file

@ -144,7 +144,7 @@ namespace timeline {
})
.matchElement ([&](GenNode const& spec, PMarker const& elm) -> bool
{ // »Matcher« : how to know we're dealing with the right object
return spec.idi == ID(elm);
return spec.idi == ID{*elm};
})
.constructFrom ([&](GenNode const& spec) -> PMarker
{ // »Constructor« : what to do when the diff mentions a new entity
@ -152,7 +152,7 @@ namespace timeline {
})
.buildChildMutator ([&](PMarker& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
{ // »Mutator« : how to apply the diff recursively to a nested scope
if (ID(target) != subID) return false; // - require match on already existing child object
if (ID{*target} != subID) return false; // - require match on already existing child object
target->buildMutator (buff); // - delegate to child to build nested TreeMutator
return true;
}))

View file

@ -56,6 +56,8 @@ namespace timeline {
, rootTrackID_{trackID}
{ }
TimelineGui::~TimelineGui() { }
/**
* actually build a TimelineWidget to enact the role

View file

@ -81,8 +81,12 @@ namespace timeline {
public:
TimelineGui (ID identity, ID trackID);
virtual ~TimelineGui();
// standard copy operations aceptable
// standard copy operations acceptable
operator ID() const { return timelineID_; }
/** actually build a TimelineWidget to enact the role

View file

@ -109,7 +109,7 @@ namespace timeline {
})
.matchElement ([&](GenNode const& spec, PMarker const& elm) -> bool
{
return spec.idi == ID(elm);
return spec.idi == ID{*elm};
})
.constructFrom ([&](GenNode const& spec) -> PMarker
{
@ -117,7 +117,7 @@ namespace timeline {
})
.buildChildMutator ([&](PMarker& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
{
if (ID(target) != subID) return false;
if (ID{*target} != subID) return false;
target->buildMutator (buff);
return true;
}))
@ -128,7 +128,7 @@ namespace timeline {
})
.matchElement ([&](GenNode const& spec, PClip const& elm) -> bool
{
return spec.idi == ID(elm);
return spec.idi == ID{*elm};
})
.constructFrom ([&](GenNode const& spec) -> PClip
{
@ -136,7 +136,7 @@ namespace timeline {
})
.buildChildMutator ([&](PClip& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
{
if (ID(target) != subID) return false;
if (ID{*target} != subID) return false;
target->buildMutator (buff);
return true;
}))
@ -147,7 +147,7 @@ namespace timeline {
})
.matchElement ([&](GenNode const& spec, PFork const& elm) -> bool
{
return spec.idi == ID(elm);
return spec.idi == ID{*elm};
})
.constructFrom ([&](GenNode const& spec) -> PFork
{
@ -155,7 +155,7 @@ namespace timeline {
})
.buildChildMutator ([&](PFork& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
{
if (ID(target) != subID) return false;
if (ID{*target} != subID) return false;
target->buildMutator (buff);
return true;
})));

View file

@ -224,6 +224,7 @@ namespace diff{
{ }
public:
explicit
ID (GenNode const& node)
: ID(node.idi)
{ }

View file

@ -147,6 +147,13 @@ namespace test{
static_assert (not sizeof(X), "### Type Debugging ###");
};
template<typename X>
void
typeDebugger(X&& x)
{
static_assert (not sizeof(X), "### Type Debugging ###");
}
namespace { // helper for printing type diagnostics

View file

@ -347,7 +347,7 @@ namespace asset {
/** marker constant denoting a NIL asset */
template<class KIND>
ID<KIND> ID<KIND>::INVALID = ID(0);
ID<KIND> ID<KIND>::INVALID = ID{0};
}} // namespace proc::asset

View file

@ -75,7 +75,7 @@ namespace proc {
class ImplFacade;
class ImplConstraint;
typedef lib::idi::EntryID<StreamType> ID;
using ID = lib::idi::EntryID<StreamType>;
Prototype const& prototype;

View file

@ -64,7 +64,8 @@ namespace test {
*/
class BasicPipe_test : public Test
{
virtual void run(Arg arg)
virtual void
run (Arg arg)
{
string pipeID = isnil (arg)? "Black Hole" : arg[1];
string streamID = 2>arg.size()? "teststream" : arg[2] ;
@ -80,7 +81,7 @@ namespace test {
void createExplicit (string pID, string sID)
{
string pID_sane (pID);
string pID_sane{pID};
normaliseID (pID_sane);
CHECK (pID_sane != pID);
@ -89,7 +90,7 @@ namespace test {
CHECK (thePipe);
CHECK (thePipe->getProcPatt());
CHECK (thePipe->getPipeID() == pID_sane);
CHECK (thePipe->getStreamID() == StreamType::ID(sID));
CHECK (thePipe->getStreamID() == StreamType::ID{sID});
CHECK (thePipe->shortDesc == pID_sane);
Asset::Ident idi = thePipe->ident;
@ -97,8 +98,8 @@ namespace test {
CHECK (contains (idi.name, thePipe->getPipeID()));
CHECK (contains (idi.name, thePipe->getStreamID()));
Category cat (idi.category);
Category refcat (STRUCT,"pipes");
Category cat{idi.category};
Category refcat{STRUCT,"pipes"};
CHECK ( cat.hasKind (STRUCT) );
CHECK ( cat.isWithin(refcat) );
}
@ -131,25 +132,25 @@ namespace test {
PPipe pipe1 = Pipe::query (""); // "the default pipe"
PPipe pipe2;
CHECK (pipe1);
CHECK (pipe1 == Session::current->defaults (Query<Pipe>()));
CHECK (pipe1 == Session::current->defaults (Query<Pipe>{}));
CHECK (pipe1->ident.category.hasKind(VIDEO));
CHECK (pipe1->getProcPatt());
PProcPatt propa = Session::current->defaults (Query<const ProcPatt>("pipe(default)"));
PProcPatt propa = Session::current->defaults (Query<const ProcPatt>{"pipe(default)"});
CHECK (propa == pipe1->getProcPatt());
// several variants to query for "the default pipe"
pipe2 = Session::current->defaults(Query<Pipe> ());
pipe2 = Session::current->defaults(Query<Pipe>{});
CHECK (pipe2 == pipe1);
pipe2 = asset::Struct::retrieve (Query<Pipe> ());
pipe2 = asset::Struct::retrieve (Query<Pipe>{});
CHECK (pipe2 == pipe1);
pipe2 = asset::Struct::retrieve (Query<Pipe> ("pipe(default)"));
pipe2 = asset::Struct::retrieve (Query<Pipe>{"pipe(default)"});
CHECK (pipe2 == pipe1);
string sID = pipe1->getStreamID(); // sort of a "default stream type"
PPipe pipe3 = Pipe::query ("stream("+sID+")");
CHECK (pipe3);
CHECK (pipe3->getStreamID() == StreamType::ID(sID));
CHECK (pipe3->getProcPatt() == Session::current->defaults (Query<const ProcPatt>("stream("+sID+")")));
CHECK (pipe3->getStreamID() == StreamType::ID{sID});
CHECK (pipe3->getProcPatt() == Session::current->defaults (Query<const ProcPatt>{"stream("+sID+")"}));
}
@ -174,7 +175,7 @@ namespace test {
// default pipe for this pattern automatically
PPipe pipe2x = Pipe::query ("pattern(another)");
CHECK (pattern2 == pipe2x->getProcPatt());
CHECK (pipe2x == Session::current->defaults (Query<Pipe>("pattern(another)")));
CHECK (pipe2x == Session::current->defaults (Query<Pipe>{"pattern(another)"}));
thePipe->switchProcPatt(pattern2);
CHECK ( dependencyCheck (thePipe, pattern2));
@ -197,7 +198,7 @@ namespace test {
PPipe pipe3x = Pipe::query ("pattern(another)");
pattern3 = pipe3x->getProcPatt(); /////TODO: transition to P<>
CHECK (pattern3 != pattern2); // because pattern2 is already unlinked...
CHECK (pipe3x == Session::current->defaults (Query<Pipe>("pattern(another)")));
CHECK (pipe3x == Session::current->defaults (Query<Pipe>{"pattern(another)"}));
CHECK (pipe3x != pipe2x); // ..we got a new default pipe for "pattern(another)" too!

View file

@ -128,16 +128,16 @@ namespace test {
retrieveConstrainedDefault (string pID, string sID)
{
PPipe pipe1 = Pipe::query (""); // "the default pipe"
CHECK ( pipe1->getStreamID() != StreamType::ID(sID),
CHECK ( pipe1->getStreamID() != StreamType::ID{sID},
"stream-ID \"%s\" not suitable for test, because "
"the default-pipe \"%s\" happens to have the same "
"stream-ID. We need it to be different",
sID.c_str(), pID.c_str()
);
string query_for_sID ("stream("+sID+")");
string query_for_sID{"stream("+sID+")"};
PPipe pipe2 = Pipe::query (query_for_sID);
CHECK (pipe2->getStreamID() == StreamType::ID(sID));
CHECK (pipe2->getStreamID() == StreamType::ID{sID});
CHECK (pipe2 != pipe1);
CHECK (pipe2 == Pipe::query (query_for_sID)); // reproducible
}
@ -146,13 +146,13 @@ namespace test {
void
failureCreatesNewDefault()
{
PPipe pipe1 = Session::current->defaults(Query<Pipe> ("")); // "the default pipe"
PPipe pipe1 = Session::current->defaults(Query<Pipe>{""}); // "the default pipe"
string new_pID = _Fmt{"dummy_%s_%i"}
% pipe1->getPipeID()
% std::rand()
; // make random new pipeID
Query<Pipe> query_for_new ("pipe("+new_pID+")");
Query<Pipe> query_for_new{"pipe("+new_pID+")"};
CHECK (!find (query_for_new)); // check it doesn't exist
PPipe pipe2 = Session::current->defaults (query_for_new); // triggers creation
@ -170,8 +170,8 @@ namespace test {
void
verifyRemoval()
{
Symbol pID ("some_pipe");
Query<Pipe> query_for_pID ("pipe("+pID+")");
Symbol pID{"some_pipe"};
Query<Pipe> query_for_pID{"pipe("+pID+")"};
size_t hash;
{
// create new pipe and declare it to be a default

View file

@ -46,7 +46,7 @@ namespace test {
print_clean ("a Sentence");
print_clean ("trailing Withespace\n \t");
print_clean ("with a \t lot\n of Whitespace");
print_clean ("with\"much (punctuation)[]!");
print_clean ("@with\".\'much ($punctuation)[]!");
print_clean ("§&Ω%€ leading garbage");
print_clean ("mixed Ω garbage");
print_clean ("Bääääh!!");