parent
c7794e7cbf
commit
828206e6b1
5 changed files with 160 additions and 50 deletions
|
|
@ -56,6 +56,7 @@
|
|||
#include "lib/util.hpp"
|
||||
|
||||
#include <boost/format.hpp>
|
||||
#include <cstdlib>
|
||||
|
||||
using boost::format;
|
||||
|
||||
|
|
@ -80,7 +81,13 @@ namespace asset {
|
|||
namespace {
|
||||
|
||||
Symbol genericIdSymbol ("id");
|
||||
Symbol seqNrPredicate ("ord");
|
||||
|
||||
inline uint
|
||||
asNumber (string const& spec)
|
||||
{
|
||||
return abs(std::atoi (spec.c_str()));
|
||||
} // returns 0 in case of unparseable number
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -116,6 +123,11 @@ namespace asset {
|
|||
}
|
||||
ENSURE (!isnil (nameID));
|
||||
|
||||
// does the query actually demand the Nth instance/element?
|
||||
string seqID = extractID (seqNrPredicate, query);
|
||||
if (!isnil (seqID) && 1 < asNumber(seqID))
|
||||
nameID += "."+seqID;
|
||||
|
||||
Category cat (STRUCT, StructTraits<STRU>::catFolder());
|
||||
return Asset::Ident (nameID, cat );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/symbol.hpp"
|
||||
#include "proc/mobject/mobject.hpp"
|
||||
#include "proc/mobject/placement-ref.hpp"
|
||||
#include "proc/mobject/output-designation.hpp"
|
||||
|
|
@ -45,9 +46,13 @@
|
|||
#include "common/configrules.hpp"
|
||||
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include <cstdlib>
|
||||
|
||||
using lumiera::query::QueryHandler;
|
||||
using lumiera::query::removeTerm;
|
||||
using lumiera::query::extractID;
|
||||
using lumiera::ConfigRules;
|
||||
using lumiera::Symbol;
|
||||
|
||||
namespace mobject {
|
||||
|
||||
|
|
@ -167,6 +172,23 @@ namespace mobject {
|
|||
HashVal resulting_targetPipeID (res? (HashVal)res->getID() : 0 );
|
||||
return resulting_targetPipeID;
|
||||
}
|
||||
|
||||
Symbol SEQNR_PREDICATE = "ord";
|
||||
|
||||
uint
|
||||
is_defaults_query_with_channel (Query<asset::Pipe> const& query4pipe)
|
||||
{
|
||||
string seqNr = extractID (SEQNR_PREDICATE, query4pipe);
|
||||
return abs(std::atoi (seqNr.c_str())); // also 0 in case of an invalid number
|
||||
}
|
||||
|
||||
Query<asset::Pipe>
|
||||
build_corresponding_sourceQuery (Query<asset::Pipe> const& query4pipe)
|
||||
{
|
||||
Query<asset::Pipe> srcQuery = query4pipe;
|
||||
removeTerm (SEQNR_PREDICATE, srcQuery);
|
||||
return srcQuery;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -52,8 +52,9 @@
|
|||
#ifndef PROC_MOBJECT_OUTPUT_MAPPING_H
|
||||
#define PROC_MOBJECT_OUTPUT_MAPPING_H
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "proc/asset/pipe.hpp"
|
||||
#include "lib/bool-checkable.hpp"
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/query.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
|
|
@ -66,8 +67,6 @@ namespace mobject {
|
|||
|
||||
namespace { // Helper to extract and rebind definition types
|
||||
|
||||
using util::contains;
|
||||
|
||||
/**
|
||||
* @internal used by OutputMapping
|
||||
* to figure out the mapping target type
|
||||
|
|
@ -120,8 +119,8 @@ namespace mobject {
|
|||
* - a functor \c DEF::output usable as function pipe-ID --> Target
|
||||
* - the concrete output-functor also defines the concrete Target type,
|
||||
* which will be returned when accessing the OutputMapping
|
||||
* - a function \c DEF::buildQuery(sourcePipeID) yielding a defaults querry,
|
||||
* to be issued in case of accessing a non existant mapping
|
||||
* - a function \c DEF::buildQuery(sourcePipeID,seqNr) yielding a (defaults)
|
||||
* query to be issued in case of accessing a non existent mapping
|
||||
*/
|
||||
template<class DEF>
|
||||
class OutputMapping
|
||||
|
|
@ -234,7 +233,7 @@ namespace mobject {
|
|||
operator== (Resolver const& a, Resolver const& b)
|
||||
{
|
||||
return a.pID_ == b.pID_;
|
||||
} // note: Resolver depends on tempate parameter DEF
|
||||
} // note: Resolver depends on template parameter DEF
|
||||
// All instances of DEF are considered equivalent!
|
||||
friend bool
|
||||
operator== (Resolver const& rr, Target const& tval)
|
||||
|
|
@ -249,7 +248,20 @@ namespace mobject {
|
|||
|
||||
Resolver operator[] (PId sourcePipeID);
|
||||
Resolver operator[] (PPipe const& pipe);
|
||||
Resolver operator[] (Query<asset::Pipe> const& query4pipe);
|
||||
Resolver operator[] (Query<asset::Pipe> query4pipe);
|
||||
|
||||
bool
|
||||
contains (PId mapping4sourcePipeID)
|
||||
{
|
||||
return util::contains (table_, mapping4sourcePipeID);
|
||||
}
|
||||
|
||||
bool
|
||||
contains (PPipe sourcePipe)
|
||||
{
|
||||
return !sourcePipe
|
||||
|| this->contains (sourcePipe->getID());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -263,7 +275,7 @@ namespace mobject {
|
|||
Resolver
|
||||
buildResolutionWrapper (HashVal tableSlot)
|
||||
{
|
||||
ASSERT (contains (table_, tableSlot));
|
||||
ASSERT (this->contains (tableSlot));
|
||||
return Resolver (*this, table_[tableSlot]);
|
||||
}
|
||||
};
|
||||
|
|
@ -280,14 +292,35 @@ namespace mobject {
|
|||
|
||||
/** delegate target pipe resolution to the rules system */
|
||||
HashVal resolveQuery (Query<asset::Pipe> const&);
|
||||
|
||||
/** detect the special case, when actually the Nth
|
||||
* solution of a defaults query is requested */
|
||||
uint is_defaults_query_with_channel (Query<asset::Pipe> const&);
|
||||
|
||||
/** ..and build the corresponding defaults source query for this case */
|
||||
Query<asset::Pipe> build_corresponding_sourceQuery (Query<asset::Pipe> const&);
|
||||
}
|
||||
|
||||
|
||||
/** standard map-style access to an OutputMapping.
|
||||
* For the given source pipe-ID the mapped target pipe-ID is fetched
|
||||
* and then handed over to the configured \c DEF::output functor, which
|
||||
* is assumed to calculate or retrieve the actual result object.
|
||||
*
|
||||
* \par default mappings
|
||||
* whenever accessing an yet non-existent mapping, a query is issued
|
||||
* behind the scenes to establish a suitable default mapping. The actual
|
||||
* query is built from a query template by the \c DEF::buildQuery function
|
||||
* and thus can be configured for the concrete usage situation of the mapping.
|
||||
* @warning depending on the actually configured defaults query, there might be
|
||||
* no solution, in which case an \em unconnected marker is retrieved and
|
||||
* stored. Thus the yielded Resolver should be checked, if in doubt.
|
||||
*/
|
||||
template<class DEF>
|
||||
inline typename OutputMapping<DEF>::Resolver
|
||||
OutputMapping<DEF>::operator[] (PId sourcePipeID)
|
||||
{
|
||||
if (!contains (table_, sourcePipeID))
|
||||
if (!contains (sourcePipeID))
|
||||
{
|
||||
// issue a defaults query to resolve this mapping first
|
||||
Query<asset::Pipe> query4pipe = DEF::buildQuery (sourcePipeID);
|
||||
|
|
@ -297,6 +330,8 @@ namespace mobject {
|
|||
}
|
||||
|
||||
|
||||
/** similar to the standard map-style access, but accepts
|
||||
* a source pipe object instead of just a pipe-ID */
|
||||
template<class DEF>
|
||||
inline typename OutputMapping<DEF>::Resolver
|
||||
OutputMapping<DEF>::operator[] (PPipe const& pipe)
|
||||
|
|
@ -311,7 +346,20 @@ namespace mobject {
|
|||
* Accessing the OutputMapping this way by query enables all kinds of
|
||||
* extended usages: It suffices that the given query somehow yields a Pipe,
|
||||
* which then is considered the mapped result and handed over to the \c DEF::output
|
||||
* functor for resolution to a result object to be returned.
|
||||
* functor for resolution to a result object to be returned.
|
||||
*
|
||||
* \par Query for the Nth default instance
|
||||
* OutputMapping provides a special behaviour for retrieving "the Nth default pipe".
|
||||
* The rationale being the request for connection to the Nth bus of a given kind, like
|
||||
* e.g. the 3rd audio subgroup or the 2nd video master. This special behaviour is triggered
|
||||
* by the predicate "ord(##)" in the query. The \em remainder of the query is supposed to
|
||||
* designate a \em default in this case, rather then querying directly for the result of
|
||||
* the mapping. Thus this remainder of the query is used to retrieve a \em source pipe,
|
||||
* which then is treated as if accessing a non-existent mapping: a suitable default
|
||||
* solution for this mapping is retrieved, but in this special case, we append the
|
||||
* given sequence number to the ID of the retrieved pipe, i.e. we get the Nth
|
||||
* (identical) solution to the aforementioned query for a default pipe.
|
||||
*
|
||||
* @note the mapped result is remembered within this mapping. Further invocations
|
||||
* with the \em same query will just fetch this stored pipe-ID and hand it
|
||||
* to the functor, without resolving the query again. You might want to
|
||||
|
|
@ -325,14 +373,29 @@ namespace mobject {
|
|||
*/
|
||||
template<class DEF>
|
||||
inline typename OutputMapping<DEF>::Resolver
|
||||
OutputMapping<DEF>::operator[] (Query<asset::Pipe> const& query4pipe)
|
||||
OutputMapping<DEF>::operator[] (Query<asset::Pipe> query4pipe)
|
||||
{
|
||||
HashVal hash4query = _mapping::slot (query4pipe);
|
||||
if (!contains (table_, hash4query))
|
||||
// need to resolve this query first
|
||||
table_[hash4query] = _mapping::resolveQuery (query4pipe);
|
||||
if (!contains (hash4query))
|
||||
{
|
||||
if (uint seqNr = _mapping::is_defaults_query_with_channel (query4pipe))
|
||||
{
|
||||
// treat the special case
|
||||
// when actually requesting the "Nth default of this kind"
|
||||
PPipe corresponding_sourcePipe
|
||||
= asset::Pipe::query(
|
||||
_mapping::build_corresponding_sourceQuery (query4pipe));
|
||||
ENSURE (corresponding_sourcePipe);
|
||||
|
||||
PId sourcePipeID = corresponding_sourcePipe->getID();
|
||||
query4pipe = DEF::buildQuery (sourcePipeID, seqNr);
|
||||
}
|
||||
|
||||
// need to resolve this query first
|
||||
table_[hash4query] = _mapping::resolveQuery (query4pipe);
|
||||
}
|
||||
|
||||
ENSURE (contains (table_, hash4query));
|
||||
ENSURE (this->contains (hash4query));
|
||||
return buildResolutionWrapper (hash4query);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,3 +15,7 @@ PLANNED "BuildSegment_test" BuildSegment_test <<END
|
|||
END
|
||||
|
||||
|
||||
TEST "Output pipe mapping" OutputMapping_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
|
|
|||
|
|
@ -23,44 +23,40 @@
|
|||
|
||||
#include "lib/test/run.hpp"
|
||||
#include "lib/test/test-helper.hpp"
|
||||
//#include "proc/asset/media.hpp"
|
||||
//#include "proc/mobject/session.hpp"
|
||||
//#include "proc/mobject/session/testclip.hpp"
|
||||
#include "proc/asset/pipe.hpp"
|
||||
//#include "proc/mobject/placement.hpp"
|
||||
#include "proc/mobject/output-mapping.hpp"
|
||||
#include "proc/asset/pipe.hpp"
|
||||
#include "lib/util.hpp"
|
||||
//#include "proc/mobject/session/mobjectfactory.hpp" ////TODO: avoidable?
|
||||
|
||||
//#include <boost/format.hpp>
|
||||
//#include <iostream>
|
||||
#include <boost/format.hpp>
|
||||
#include <string>
|
||||
|
||||
//using boost::format;
|
||||
//using lumiera::Time;
|
||||
//using util::contains;
|
||||
using boost::format;
|
||||
using util::isnil;
|
||||
using std::string;
|
||||
//using std::cout;
|
||||
|
||||
|
||||
namespace mobject {
|
||||
namespace test {
|
||||
|
||||
//using asset::VIDEO;
|
||||
using asset::Pipe;
|
||||
using asset::PPipe;
|
||||
|
||||
typedef asset::ID<Pipe> PID;
|
||||
|
||||
namespace {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************
|
||||
* @test create a synthetic / example mapping to verify
|
||||
* generic mapping behaviour
|
||||
/*********************************************************************************
|
||||
* @test create a synthetic / example mapping to verify generic mapping behaviour.
|
||||
* We're creating a custom mapping type here, for this test only: The
|
||||
* struct DummyDef provides a "definition context" for this custom mapping
|
||||
* - there is a function to retrieve the actual target object
|
||||
* for any target pipe stored in the mapping. In this case,
|
||||
* we just extract the name-ID string from the pipe as result
|
||||
* - as an additional sideeffect, this DummyDef::output functor
|
||||
* also defines the Target type of this custom mapping to be std::string
|
||||
* - DummyDef::buildQuery provides a template query, to be issued whenever
|
||||
* a yet nonexistent mapping is requested. In this special case here
|
||||
* we query for a pipe with the name "master_XXX", where XXX denotes
|
||||
* the stream-type of the source pipe to be mapped.
|
||||
*
|
||||
* @see mobject::OutputDesignation
|
||||
* @see mobject::session::Binding
|
||||
|
|
@ -76,9 +72,11 @@ namespace test {
|
|||
}
|
||||
|
||||
Query<Pipe>
|
||||
buildQuery (PID sourcePipeID)
|
||||
buildQuery (PID sourcePipeID, uint seqNr =0)
|
||||
{
|
||||
UNIMPLEMENTED ("fabricate defaults query for unit-test");
|
||||
PPipe srcP = Pipe::lookup (sourcePipeID);
|
||||
format queryPattern ("id(master_%1%), stream(%1%), ord(%2%)");
|
||||
return Query<Pipe> (queryPattern % srcP->getStreamID() % seqNr);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -110,8 +108,14 @@ namespace test {
|
|||
CHECK (map[p1].isValid());
|
||||
CHECK (map[p1]);
|
||||
|
||||
CHECK (!map.contains (pX));
|
||||
CHECK (!map.contains (p2));
|
||||
|
||||
// create an unconnected mapping
|
||||
map[pX].disconnect();
|
||||
CHECK (map.contains (pX));
|
||||
CHECK (!map[pX].isValid());
|
||||
CHECK (!map[pX]);
|
||||
CHECK (!map[p2]);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -128,22 +132,22 @@ namespace test {
|
|||
Mapping m2(m1);
|
||||
CHECK (!isnil (m2));
|
||||
CHECK (1 == m2.size());
|
||||
CHECK (m1[p1] == "hairy");
|
||||
CHECK (m2[p1] == "hairy");
|
||||
CHECK (m1[pi] == "hairy");
|
||||
CHECK (m2[pi] == "hairy");
|
||||
|
||||
m1[pi] = p2;
|
||||
CHECK (m1[p1] == "furry");
|
||||
CHECK (m2[p1] == "hairy");
|
||||
CHECK (m1[pi] == "furry");
|
||||
CHECK (m2[pi] == "hairy");
|
||||
|
||||
m2 = m1;
|
||||
CHECK (m1[p1] == "furry");
|
||||
CHECK (m2[p1] == "furry");
|
||||
CHECK (m1[pi] == "furry");
|
||||
CHECK (m2[pi] == "furry");
|
||||
|
||||
m1.clear();
|
||||
CHECK (isnil(m1));
|
||||
CHECK (!isnil(m2));
|
||||
CHECK (m2[p1] == "furry");
|
||||
CHECK (!m1[p1].isValid());
|
||||
CHECK (m2[pi] == "furry");
|
||||
CHECK (!m1.contains (pi));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -156,17 +160,22 @@ namespace test {
|
|||
PPipe p1 = Pipe::query("stream(hairy)");
|
||||
PPipe p2 = Pipe::query("stream(furry)");
|
||||
|
||||
CHECK (map[p1] == "master(hairy)");
|
||||
CHECK (map[p2] == "master(furry)");
|
||||
CHECK (map[p1] == "master_hairy");
|
||||
CHECK (map[p2] == "master_furry");
|
||||
|
||||
// create new mapping to an explicitly queried target
|
||||
Query<Pipe> some_pipe ("pipe(super_curly)");
|
||||
CHECK (map[some_pipe] == "super_curly");
|
||||
|
||||
// create a new mapping to the 2nd master for "furry" data
|
||||
Query<Pipe> special_bus ("stream(furry), ord(2)");
|
||||
CHECK (map[special_bus] == "master.2(furry)");
|
||||
CHECK (map[special_bus] == "master_furry.2");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** Register this test class... */
|
||||
LAUNCHER (OutputMapping_test, "unit session");
|
||||
LAUNCHER (OutputMapping_test, "unit session builder");
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue