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:
parent
e81b0592d3
commit
fa6ba76f85
14 changed files with 92 additions and 69 deletions
|
|
@ -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";
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
})));
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ namespace timeline {
|
|||
, rootTrackID_{trackID}
|
||||
{ }
|
||||
|
||||
TimelineGui::~TimelineGui() { }
|
||||
|
||||
|
||||
/**
|
||||
* actually build a TimelineWidget to enact the role
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
})));
|
||||
|
|
|
|||
|
|
@ -224,6 +224,7 @@ namespace diff{
|
|||
{ }
|
||||
|
||||
public:
|
||||
explicit
|
||||
ID (GenNode const& node)
|
||||
: ID(node.idi)
|
||||
{ }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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!
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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!!");
|
||||
|
|
|
|||
Loading…
Reference in a new issue