merge diagnostic facilities

This commit is contained in:
Fischlurch 2011-12-31 06:49:31 +01:00
commit 06c7c27252
47 changed files with 2137 additions and 219 deletions

View file

@ -21,30 +21,101 @@
// 1/11 - how to fetch the path of the own executable -- at least under Linux?
// 10/11 - simple demo using a pointer and a struct
// 11/11 - using the boost random number generator(s)
// 12/11 - how to detect if string conversion is possible?
#include "lib/util.hpp"
#include <iostream>
#include <boost/lexical_cast.hpp>
#include <boost/random/linear_congruential.hpp>
using boost::lexical_cast;
#include <string>
using std::string;
using std::cout;
using std::endl;
typedef char Yes_t;
struct No_t { char more_than_one[4]; };
template<typename TY>
struct _can_convertToString
{
static TY & probe();
static Yes_t check(string);
static No_t check(...);
public:
static const bool value = (sizeof(Yes_t)==sizeof(check(probe())));
};
class SubString
: public string
{
public:
SubString() : string("sublunar") { }
};
class Something { };
struct SomehowStringy
{
operator string() { return "No such thing"; }
};
struct SomehowSub
{
operator SubString() { return SubString(); }
};
class SomehowSubSub
: public SomehowSub
{
};
template<typename TY>
bool
investigate (TY const&)
{
return _can_convertToString<TY>::value;
}
#define SHOW_CHECK(_EXPR_) cout << STRINGIFY(_EXPR_) << "\t : " << (investigate(_EXPR_)? "Yes":"No") << endl;
int
main (int cnt, char* argv[])
main (int, char**)
{
int32_t seed = (2 == cnt)? lexical_cast<int32_t> (argv[1]) : 42;
SHOW_CHECK (string("nebbich"));
SHOW_CHECK ("gurks");
SHOW_CHECK (23.34);
SHOW_CHECK (23);
boost::rand48 ranGen(seed);
string urgs("urgs");
string & urgs_ref (urgs);
string const& urgs_const_ref (urgs);
string * urgs_ptr = &urgs;
cout << "seed = "<< seed << endl;
for (uint i=0; i< 100; ++i)
cout << ranGen() % CHAR_MAX <<"__";
SHOW_CHECK (urgs_ref);
SHOW_CHECK (urgs_const_ref);
SHOW_CHECK (*urgs_ptr);
SubString sub;
Something thing;
const SomehowStringy stringy = SomehowStringy();
SomehowSubSub subsub;
SubString const& subRef(subsub);
SHOW_CHECK (sub);
SHOW_CHECK (thing);
SHOW_CHECK (stringy);
SHOW_CHECK (subsub);
SHOW_CHECK (subRef);
cout << "\n.gulp.\n";

View file

@ -83,8 +83,8 @@ namespace backend {
/**
* Descriptor holding the global informations,
* needed for further handling this media within Lumiera.
* Descriptor holding the global information record
* required for further handling this kind of media within Lumiera.
*/
struct MediaDesc
{

View file

@ -188,7 +188,7 @@ namespace backend {
/** @note by design there is no possibility to find out
* just based on the thread handle, if the thread is alive.
* just based on the thread handle if some thread is alive.
* We define our own accounting here based on the internals
* of the thread wrapper. This will break down, if you mix
* uses of the C++ wrapper with the raw C functions. */

View file

@ -37,7 +37,7 @@
#include <errno.h>
/**
* @file
* @file threads.c
*
*/
@ -93,17 +93,17 @@ thread_loop (void* thread)
do {
lumiera_threadpool_release_thread(t);
LUMIERA_CONDITION_WAIT (t->state != LUMIERA_THREADSTATE_IDLE);
INFO (threads, "Thread awaken with state %s", lumiera_threadstate_names[t->state]);
TRACE (threads, "Thread awaken with state %s", lumiera_threadstate_names[t->state]);
// NULL function means: no work to do
INFO (threads, "function %p", t->function);
TRACE (threads, "function %p", t->function);
if (t->function)
t->function (t->arguments);
TRACE (threads, "function done");
if (t->kind & LUMIERA_THREAD_JOINABLE)
{
INFO (threads, "Thread zombified");
TRACE (threads, "Thread zombified");
/* move error state to data the other thread will it pick up from there */
t->arguments = (void*)lumiera_error ();
t->state = LUMIERA_THREADSTATE_ZOMBIE;
@ -111,14 +111,14 @@ thread_loop (void* thread)
LUMIERA_CONDITION_SIGNAL;
LUMIERA_CONDITION_WAIT (t->state == LUMIERA_THREADSTATE_JOINED);
INFO (threads, "Thread joined");
TRACE (threads, "Thread joined");
}
} while (t->state != LUMIERA_THREADSTATE_SHUTDOWN);
// SHUTDOWN state
INFO (threads, "Thread Shutdown");
TRACE (threads, "Thread done.");
}
//////////////////////////////////////////////////////////////////////TICKET #844 no error must be pending here, else do app shutdown
return 0;

View file

@ -37,7 +37,7 @@
/**
* @file
* @file threads.h
*
*/

View file

@ -1,5 +1,5 @@
/*
Notification(Proxy) - public service allowing to push informations into the GUI
Notification(Proxy) - public service allowing to push information into the GUI
Copyright (C) Lumiera.org
2008, Hermann Vosseler <Ichthyostega@web.de>

View file

@ -1,5 +1,5 @@
/*
NotificationService - public service allowing to push informations into the GUI
NotificationService - public service allowing to push information into the GUI
Copyright (C) Lumiera.org
2008, Hermann Vosseler <Ichthyostega@web.de>

View file

@ -1,5 +1,5 @@
/*
NOTIFICATION-SERVICE.hpp - public service allowing to push informations into the GUI
NOTIFICATION-SERVICE.hpp - public service allowing to push information into the GUI
Copyright (C) Lumiera.org
2008, Hermann Vosseler <Ichthyostega@web.de>

View file

@ -78,7 +78,7 @@ public:
* from the session / current timeline to be displayed. It doesn't
* make sense to display raw time values here, as each timeline might
* turn out to have a different origin; this is the result of resolving
* a placement, and only the session has the necessary informations...
* a placement, and only the session has the necessary information...
*/
Offset get_time_offset() const;

View file

@ -1,5 +1,5 @@
/*
GUINOTIFICATION-FACADE.h - access point for pushing informations into the GUI
GUINOTIFICATION-FACADE.h - access point for pushing information into the GUI
Copyright (C) Lumiera.org
2008, Hermann Vosseler <Ichthyostega@web.de>
@ -24,7 +24,7 @@
** Major public Interface of the Lumiera GUI. While generally speaking, the GUI
** controls the application and thus acts on its own, it exposes some services
** usable by scripts or the two lower layers. The main purpose of these services
** is to push informations and status updates into the GUI.
** is to push status updates and similar information up into the GUI.
**
** @see notification-service.hpp implementation
** @see gui::GuiFacade

View file

@ -21,15 +21,15 @@
*/
/** @file diagnostic-context.hpp
** Facility for collecting diagnostic informations explicitly.
** Facility for collecting diagnostic context information explicitly.
** Unlike a trace logging run, this facility is intended to be fed explicitly with
** diagnostic information describing the currently ongoing operation in a semantic
** high-level manner. The rationale is to pinpoint \em those informations which aren't
** obvious when just looking at a callstack with the debugger. Instances of the class
** DiagnosticContext should be placed explicitly as automatic (stack) variable
** into selected relevant scopes; these "information frames" can be accessed
** from an enclosed scope as a per-thread stack. DiagnosticContext provides
** an controlled environment for adding diagnostic code on demand; typically
** high-level manner. The rationale is to pinpoint \em those pieces of information,
** which aren't obvious when just looking at a callstack with the debugger.
** Instances of the class DiagnosticContext should be placed explicitly as automatic
** (stack) variable into selected relevant scopes; these "information frames" could
** be accessed from an enclosed scope as a per-thread stack. DiagnosticContext
** provides an controlled environment for adding diagnostic code on demand; typically
** to be configured such as to resolve into an empty class for release builds.
**
** As of 2/10, this is an experimental feature in evaluation. To start with,
@ -57,26 +57,23 @@ namespace lib {
#ifdef NOBUG_MODE_ALPHA ////////////////////////TODO don't we need the handle in BETA builds for resource logging?
/**
* Diagnostic data frame to collect specific informations concerning a scope.
* Diagnostic data frame to collect specific information concerning a scope.
* To be placed explicitly as an automatic (stack) variable. Provides a controlled
* environment for hooking up diagnostic code. Within each thread, a stack of
* such information frames concerning nested scopes is maintained automatically.
* It can be accessed via static API.
* @warning never store this into global data structures.
* @note as of 2/10 used for housing the NoBug resource tracker handle
* in conjunction with object monitor based locking
* @see sync.hpp
* @warning relies on thread-local access; never use this within global data structures.
*/
template<typename VAL>
class DiagnosticContext
: boost::noncopyable
{
typedef nobug_resource_user* Handle;
typedef ThreadLocalPtr<DiagnosticContext> ThreadLocalAccess;
typedef std::vector<VAL> ValSequence;
Handle handle_;
const VAL value_;
DiagnosticContext * const prev_;
/** embedded thread local pointer
@ -90,8 +87,8 @@ namespace lib {
public:
DiagnosticContext()
: handle_(0)
DiagnosticContext(VAL const& value_to_log = VAL())
: value_(value_to_log)
, prev_(current().get())
{
current().set (this);
@ -104,9 +101,9 @@ namespace lib {
}
operator Handle* () ///< payload: NoBug resource tracker user handle
operator VAL const&() ///< access the payload by conversion
{
return &handle_;
return value_;
}
/** accessing the innermost diagnostic context created */
@ -116,39 +113,33 @@ namespace lib {
DiagnosticContext* innermost = current().get();
if (!innermost)
throw lumiera::error::Logic ("Accessing Diagnostic context out of order; "
"an instance should have been created in "
"an instance should have been created within "
"an enclosing scope");
return *innermost;
}
};
#else /* not NOBUG_ALPHA */
/**
* Disabled placeholder for the Diagnostic context, not used in release builds.
*/
class DiagnosticContext
: boost::noncopyable
{
public:
operator Handle* () ///< payload: NoBug resource tracker user handle
/** snapshot of the current stack of diagnostic frames
* @return vector with all the payload values currently
* on the thread-local diagnostic stack. Might
* be empty. Values start with frame next to
* the current scope and end with outermost.
*/
static ValSequence
extractStack()
{
return 0;
}
/** accessing the innermost diagnostic context created */
static DiagnosticContext&
access ()
{
UNIMPLEMENTED ("how to disable DiagnosticContext with minimum overhead");
ValSequence loggedValues;
DiagnosticContext* next = current().get();
while (next)
{
loggedValues.push_back (*next);
next = next->prev_;
}
return loggedValues;
}
};
#endif /* NOBUG_ALPHA? */

259
src/lib/format-string.cpp Normal file
View file

@ -0,0 +1,259 @@
/*
FormatString - string template formatting based on boost::format
Copyright (C) Lumiera.org
2011, Hermann Vosseler <Ichthyostega@web.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* *****************************************************/
/** @file format-string.cpp
** Implementation for printf-style formatting, based on boost::format.
** This file holds the generic implementation of our format frontend,
** which actually just invokes boost::format. The corresponding header
** format-string.hpp provides some template functions and classes,
** either invoking a custom string conversion, or passing primitive
** values down unaltered.
**
** Here, we define explicit specialisations for the frontend to invoke,
** which in turn just pass on the given argument value to the embedded
** boost::format object, which in turn dumps the formatted result
** into an embedded string stream.
**
** To avoid exposing boost::format in the frontend header, we use an
** opaque buffer of appropriate size to piggyback the formatter object.
** @warning unfortunately this requires a hard coded buffer size constant
** in the front-end, which we define there manually, based on
** the current implementation layout found in the boost libraries.
** If Boost eventually changes the implementation, the assertion
** in our constructor will trigger.
**
** @see FormatString_test
**
*/
#include "lib/error.hpp"
#include "lib/format-util.hpp"
#include "lib/format-string.hpp"
#include <boost/static_assert.hpp>
#include <boost/format.hpp>
#include <iostream>
namespace util {
using boost::format;
namespace { // implementation details...
inline boost::format&
accessImpl (char* buffer)
{
return reinterpret_cast<boost::format&> (*buffer);
}
inline void
destroyImpl (char* buffer)
{
accessImpl(buffer).~format();
}
/** in case the formatting of a (primitive) value fails,
* we try to supply an error indicator instead */
void
pushFailsafeReplacement (char* formatter, const char* errorMsg =NULL)
try {
string placeholder("<Error");
if (errorMsg){
placeholder += ": ";
placeholder += errorMsg;
}
placeholder += ">";
accessImpl(formatter) % placeholder;
}
ERROR_LOG_AND_IGNORE (progress, "Supplying placeholder for problematic format parameter")
inline void
suppressInsufficientArgumentErrors (char* formatter)
{
using namespace boost::io;
accessImpl(formatter).exceptions (all_error_bits ^ too_few_args_bit);
}
}//(End) implementation details
/** Build a formatter object based on the given format string.
* The actual implementation is delegated to an boost::format object,
* which is placement-constructed into an opaque buffer embedded into
* this object. Defining the necessary size for this buffer relies
* on a implementation details of boost::format (and might break)
*/
_Fmt::_Fmt (string formatString)
try {
BOOST_STATIC_ASSERT (sizeof(boost::format) <= FORMATTER_SIZE);
new(formatter_) boost::format(formatString);
suppressInsufficientArgumentErrors (formatter_);
}
catch (boost::io::bad_format_string& syntaxError)
{
throw lumiera::error::Fatal (syntaxError
, _Fmt("Format string '%s' is broken") % formatString
, LUMIERA_ERROR_FORMAT_SYNTAX);
}
_Fmt::~_Fmt ()
{
destroyImpl (formatter_);
}
/** @internal access point for the frontend,
* allowing to push a single parameter value down
* into the implementation for the actual formatting.
* Only selected primitive types are handled directly this way,
* while all custom conversions, the handling of pointers and the
* fallback (showing a type string) is done in the frontend.
* @note we need to generate instantiations of this template function
* explicitly for all basic types to be supported for direct handling,
* otherwise we'll get linker errors. Lumiera uses the ``inclusion model''
* for template instantiation, which means there is no compiler magic
* involved and a template function either needs to be \em defined in
* a header or explicitly instantiated in some translation unit.
*/
template<typename VAL>
void
_Fmt::format (const VAL val, Implementation& formatter)
try {
accessImpl(formatter) % val;
}
catch (boost::io::too_many_args& argErr)
{
WARN (progress, "Format: excess argument '%s' of type %s ignored."
, cStr(str(val))
, cStr(tyStr(val)));
}
catch (std::exception& failure)
{
_clear_errorflag();
WARN (progress, "Format: Parameter '%s' causes problems: %s"
, cStr(str(val))
, failure.what());
pushFailsafeReplacement (formatter, failure.what());
}
catch (...)
{
_clear_errorflag();
WARN (progress, "Format: Unexpected problems accepting format parameter '%s'", cStr(str(val)));
pushFailsafeReplacement (formatter);
}
/* ===== explicitly supported =================== */
template void _Fmt::format (const char, Implementation&);
template void _Fmt::format (const uchar, Implementation&);
template void _Fmt::format (const int, Implementation&);
template void _Fmt::format (const uint, Implementation&);
template void _Fmt::format (const short, Implementation&);
template void _Fmt::format (const ushort, Implementation&);
template void _Fmt::format (const int64_t, Implementation&);
template void _Fmt::format (const uint64_t,Implementation&);
template void _Fmt::format (const float, Implementation&);
template void _Fmt::format (const double, Implementation&);
template void _Fmt::format (const string, Implementation&);
template void _Fmt::format (const void *, Implementation&);
template void _Fmt::format (const char *, Implementation&);
/** @remarks usually the _Fmt helper is used inline
* at places where a string is expected. The '%' operator
* used to fed parameters has a higher precedence than the
* assignment or comparison operators, ensuring that all parameters
* are evaluated and formatted prior to receiving the formatted result
*/
_Fmt::operator string() const
try {
return accessImpl(formatter_).str();
}
catch (std::exception& failure)
{
_clear_errorflag();
WARN (progress, "Format: Failure to receive formatted result: %s", failure.what());
return "<formatting failure>";
}
catch (...)
{
_clear_errorflag();
WARN (progress, "Format: Unexpected problems while formatting output.");
return "<unexpected problems>";
}
/** send the formatted buffer directly to the output stream.
* @note this is more efficient than creating a string and outputting that,
* because boost::format internally uses a string-stream to generate
* the formatted representation, relying on the C++ output framework
*/
std::ostream&
operator<< (std::ostream& os, _Fmt const& fmt)
try {
return os << accessImpl(fmt.formatter_);
}
catch(std::exception& failure)
{
_clear_errorflag();
WARN (progress, "Format: Failure when outputting formatted result: %s", failure.what());
return os << "<formatting failure>";
}
catch(...)
{
_clear_errorflag();
WARN (progress, "Format: Unexpected problems while producing formatted output.");
return os << "<unexpected problems>";
}
LUMIERA_ERROR_DEFINE (FORMAT_SYNTAX, "Syntax error in format string for boost::format");
} // namespace util

421
src/lib/format-string.hpp Normal file
View file

@ -0,0 +1,421 @@
/*
FORMAT-STRING.hpp - string template formatting based on boost::format
Copyright (C) Lumiera.org
2011, Hermann Vosseler <Ichthyostega@web.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/** @file format-string.hpp
** Front-end for printf-style string template interpolation.
** While the actual implementation just delegates to boost::format, this front-end
** hides the direct dependency, additionally invokes a custom toSting conversion
** whenever possible, provides a direct automatic conversion to the formatted result
** to string and catches any exceptions.
**
** This front-end is used pervasively for diagnostics and logging, so keeping down the
** compilation and object size cost and reliably handling any error is more important
** than the (small) performance gain of directly invoking boost::format (which is
** known to be 10 times slower than printf anyway).
**
** \par Implementation notes
** To perform the formatting, usually a \c _Fmt object is created as an anonymous
** temporary, but it may as well be stored into a variable. Copying is not permitted.
** Individual parameters are then fed for formatting through the \c '%' operator.
** Each instance of _Fmt uses its own embedded boost::format object for implementation,
** but this formatter resides within an opaque buffer embedded into the frontend object.
** The rationale for this admittedly tricky approach is to confine any usage of boost::format
** to the implementation translation unit (format-string.cpp).
**
** The implementation is invoked by the frontend through a set of explicit specialisations
** for all the relevant \em primitive data types. For custom types, we prefer to invoke
** operator string() if possible, which is determined by a simple metaprogramming test,
** which is defined in lib/meta/util.pp, without relying on boost. As a fallback, for
** all other types without built-in or custom string conversion, we use the mangled
** type string produced by RTTI.
**
** The compile time and object size overhead incurred by using this header was verified
** to be negligible, in comparison to using boost::format. When compiling a demo example
** on x86_64, the following executable sizes could be observed:
**
** debug stripped
** just string concatenation ............... 42k 8.8k
** including and using format-string.hpp ... 50k 9.4k
** including and using boost::format ....... 420k 140k
**
** In addition, we need to take the implementation translation unit (format-string.cpp)
** into account, which is required once per application and contains the specialisations
** for all primitive types. In the test showed above, the corresponding object file
** had a size of 1300k (with debug information) resp. 290k (stripped).
**
** \par Usage
** The syntax of the format string is defined by boost::format and closely mimics
** the printf formatting directives. The notable difference is that boost::format
** uses the C++ stream output framework, and thus avoiding the perils of printf.
** The individual formatting placeholders just set the corresponding flags on
** an embedded string stream, thus the actual parameter types cause the
** selection of a suitable format, not the definitions within the
** format string.
**
** An illegal format string will raise an error::Fatal. Any other error during usage of
** the formatter is caught, logged and suppressed, inserting an error indicator into
** the formatted result instead
**
** A formatter is usually created as an anonymous object, at places where a string
** is expected. An arbitrary number of parameters is then supplied using the \c '%' operator.
** The result can be obtained
** - by string conversion
** - by feeding into an output stream.
**
** Code example:
** \code
** double total = 22.9499;
** const char * currency = "€";
** cout << _Fmt("price %+5.2f %s") % total % currency << endl;
** \endcode
**
** @remarks See the unit-test for extensive usage examples and corner cases.
** The header format-util.hpp provides an alternative string conversion,
** using a bit of boost type traits and lexical_cast, but no boost::format.
** @warning not suited for performance critical code. About 10 times slower than printf.
**
** @see FormatString_test
** @see format-util.hpp
**
*/
#ifndef UTIL_FORMAT_STRING_H
#define UTIL_FORMAT_STRING_H
#include "lib/error.hpp"
#include "lib/meta/util.hpp"
#include "lib/meta/size-trait.hpp"
#include <string>
#include <typeinfo>
#include <boost/noncopyable.hpp>
#include <boost/utility/enable_if.hpp>
namespace std { // forward declaration to avoid including <iostream>
template<typename C>
class char_traits;
template<typename C, class _TRAITS>
class basic_ostream;
typedef basic_ostream<char, char_traits<char> > ostream;
}
namespace util {
using std::string;
using boost::enable_if;
typedef unsigned char uchar;
LUMIERA_ERROR_DECLARE (FORMAT_SYNTAX); ///< "Syntax error in format string for boost::format"
/**
* A front-end for using printf-style formatting.
* Values to be formatted can be supplied through the
* operator%. Custom defined string conversions on objects
* will be used, any errors while invoking the format operation
* will be suppressed. The implementation is based on boost::format,
* but kept opaque to keep code size and compilation times down.
* @see FormatString_test
*/
class _Fmt
: boost::noncopyable
{
/** size of an opaque implementation Buffer */
enum{ FORMATTER_SIZE = lib::meta::SizeTrait::BOOST_FORMAT };
typedef char Implementation[FORMATTER_SIZE];
/** @internal buffer to hold a boost::format */
mutable Implementation formatter_;
template<typename VAL>
static void format (const VAL, Implementation&);
/** helper to prepare parameters for inclusion */
template<typename VAL, class SEL =void>
struct Converter;
public:
~_Fmt ();
_Fmt (string formatString);
operator string() const; ///< get the formatted result
template<typename VAL>
_Fmt&
operator% (VAL const&);
friend std::ostream&
operator<< (std::ostream& os, _Fmt const&);
friend bool operator== (_Fmt const&, _Fmt const&);
friend bool operator== (_Fmt const&, string const&);
friend bool operator== (_Fmt const&, const char * const);
friend bool operator== (string const& , _Fmt const&);
friend bool operator== (const char * const, _Fmt const&);
template<typename X>
friend bool operator != (_Fmt const& fmt, X const& x) { return !(fmt == x); }
template<typename X>
friend bool operator != (X const& x, _Fmt const& fmt) { return !(x == fmt); }
};
/* ===== forwarding into the implementation ====== */
/** The percent operator (\c '%' ) is used do feed parameter values
* to be included into the formatted result, at the positions marked
* by printf-style placeholders within the format string.
*
* \par type specific treatment
* Basic types (numbers, chars, strings) are passed to the implementation
* (= boost::format) literally. For custom types, we try to use a custom
* string conversion, if applicable. Non-NULL pointers will be dereferenced,
* with the exception of C-Strings and \c void*. Any other type gets just
* translated into a type-ID (using the mangled RTTI info). In case of errors
* during the conversion, a string representation of the error is returned
* @param val arbitrary value or pointer to be included into the result
* @warning you need to provide exactly the right number of parameters,
* i.e. matching the number of fields in the format string.
*/
template<typename VAL>
inline _Fmt&
_Fmt::operator% (VAL const& val)
{
Converter<VAL>::dump (val, formatter_);
return *this;
}
namespace { // helpers to pick a suitable specialisation....
/**
* by default we don't allow to
* treat any types directly by boost::format.
* As fallback we rather just produce a type-ID
*/
template<typename X>
struct _allow_call { enum{ value = false };};
/* the following definitions enable some primitive types
* to be handed over to the boost::format implementation */
template<> struct _allow_call<string> { enum{ value = true }; };
template<> struct _allow_call<char> { enum{ value = true }; };
template<> struct _allow_call<uchar> { enum{ value = true }; };
template<> struct _allow_call<int> { enum{ value = true }; };
template<> struct _allow_call<uint> { enum{ value = true }; };
template<> struct _allow_call<short> { enum{ value = true }; };
template<> struct _allow_call<ushort> { enum{ value = true }; };
template<> struct _allow_call<int64_t> { enum{ value = true }; };
template<> struct _allow_call<uint64_t>{ enum{ value = true }; };
template<> struct _allow_call<float> { enum{ value = true }; };
template<> struct _allow_call<double> { enum{ value = true }; };
template<typename X>
struct _shall_format_directly
{
typedef typename lib::meta::UnConst<X>::Type BaseType;
enum{ value = _allow_call<BaseType>::value };
};
template<typename X>
struct _shall_convert_toString
{
enum{ value = ! _shall_format_directly<X>::value
&& lib::meta::can_convertToString<X>::value
};
};
inline void
_clear_errorflag()
{
const char* errID = lumiera_error();
TRACE_IF (errID, progress, "Lumiera errorstate '%s' cleared.", errID);
}
inline string
_log_and_stringify (std::exception const& ex)
{
_clear_errorflag();
WARN (progress, "Error while invoking custom string conversion: %s", ex.what());
try {
return string("<string conversion failed: ")+ex.what()+">";
}
catch(...) { /* secondary errors ignored */ }
return "(formatting failure)";
}
inline string
_log_unknown_exception()
{
const char* errID = lumiera_error();
if (errID)
ERROR (progress, "Unknown error while invoking custom string conversion. Lumiera error flag = %s", errID);
else
ERROR (progress, "Unknown error while invoking custom string conversion. No Lumiera error flag set.");
return "<Unknown error in string conversion>";
}
}//(End) guards/helpers
/* === explicit specialisations to control the kind of conversion === */
/** default/fallback: just indicate the (static) type */
template<typename VAL, class SEL>
struct _Fmt::Converter
{
static void
dump (VAL const&, Implementation& impl)
{
format (string("«")+typeid(VAL).name()+"»", impl);
}
};
template<typename VAL>
struct _Fmt::Converter<VAL*>
{
static void
dump (const VAL *pVal, Implementation& impl)
{
if (pVal)
Converter<VAL>::dump(*pVal, impl);
else
format ("<null>", impl);
}
};
template<>
struct _Fmt::Converter<void *>
{
static void
dump (const void* address, Implementation& impl)
{
format (address, impl);
}
};
template<>
struct _Fmt::Converter<const char *>
{
static void
dump (const char* cString, Implementation& impl)
{
format (cString? cString : "↯", impl);
}
};
/** some custom types explicitly provide a string representation */
template<typename VAL>
struct _Fmt::Converter<VAL, typename enable_if< _shall_convert_toString<VAL> >::type>
{
static void
dump (VAL const& val, Implementation& impl)
try {
format (string(val), impl);
}
catch(std::exception const& ex)
{
format (_log_and_stringify(ex), impl);
}
catch(...)
{
format (_log_unknown_exception(), impl);
}
};
/** some basic types are directly forwarded down to the implementation;
* @note this requires explicit specialisations in format-string.cpp */
template<typename VAL>
struct _Fmt::Converter<VAL, typename enable_if< _shall_format_directly<VAL> >::type>
{
static void
dump (const VAL val, Implementation& impl)
{
format (val, impl);
}
};
/* === comparison of formatter objects === */
inline bool
operator== (_Fmt const& left, _Fmt const& right)
{
return string(left) == string(right);
}
inline bool
operator== (_Fmt const& fmt, string const& str)
{
return string(fmt) == str;
}
inline bool
operator== (_Fmt const& fmt, const char * const cString)
{
return string(fmt) == string(cString);
}
inline bool
operator== (string const& str, _Fmt const& fmt)
{
return fmt == str;
}
inline bool
operator== (const char * const cString, _Fmt const& fmt)
{
return fmt == cString;
}
} // namespace util
#endif

View file

@ -1,5 +1,5 @@
/*
FORMAT.hpp - helpers for formatting and diagnostics
FORMAT-UTIL.hpp - helpers for formatting and diagnostics
Copyright (C) Lumiera.org
2009, Hermann Vosseler <Ichthyostega@web.de>
@ -21,20 +21,25 @@
*/
/** @file format.hpp
/** @file format-util.hpp
** Collection of small helpers and convenience shortcuts for diagnostics & formatting.
** - util::str() performs a failsafe to-String conversion, thereby preferring a
** built-in conversion operator, falling back to just a mangled type string.
** - util::tyStr() generates a string corresponding to the type of the given object.
** Currently just implemented through the mangled RTTI type string
**
** @todo we could add a facade to boost::format here, see Ticket #166
** @see FormatHelper_test
** @see format-string.hpp frontend for boost::format, printf-style
**
*/
#ifndef UTIL_FORMAT_H
#define UTIL_FORMAT_H
#ifndef LIB_FORMAT_UTIL_H
#define LIB_FORMAT_UTIL_H
//#include "lib/util.hpp"
#include "lib/meta/trait.hpp"
#include "lib/symbol.hpp"
#include "lib/util.hpp"
#include <string>
#include <cstring>
@ -46,61 +51,65 @@
namespace util {
using boost::enable_if;
using lib::meta::can_ToString;
using lib::meta::can_lexical2string;
using lib::Symbol;
using boost::enable_if;
using boost::disable_if;
using util::isnil;
using std::string;
namespace { // we need to guard the string conversion
// to avoid a compiler error in case the type isn't convertible....
template<typename X>
inline string
invoke_2string ( typename enable_if< can_ToString<X>,
X >::type const& val)
{
return string(val);
}
template<typename X>
inline string
invoke_2string ( typename disable_if< can_ToString<X>,
X >::type const&)
{
return "";
}
struct use_StringConversion : can_ToString<X> { };
template<typename X>
inline string
invoke_indirect2string ( typename enable_if< can_lexical2string<X>,
X >::type const& val)
{
try { return boost::lexical_cast<string> (val); }
catch(...) { return ""; }
}
struct use_LexicalConversion
{
enum { value = can_lexical2string<X>::value
&& !can_ToString<X>::value
};
};
/** helper: reliably get some string representation for type X */
template<typename X, typename COND =void>
struct _InvokeFailsafe
{
static string toString (X const&) { return ""; }
};
template<typename X>
inline string
invoke_indirect2string ( typename disable_if< can_lexical2string<X>,
X >::type const&)
{
return "";
}
}
struct _InvokeFailsafe<X, typename enable_if< use_StringConversion<X> >::type>
{
static string
toString (X const& val)
try { return string(val); }
catch(...) { return ""; }
};
template<typename X>
struct _InvokeFailsafe<X, typename enable_if< use_LexicalConversion<X> >::type>
{
static string
toString (X const& val)
try { return boost::lexical_cast<string> (val); }
catch(...) { return ""; }
};
}//(End) guards/helpers
/** try to get an object converted to string.
* An custom/standard conversion to string is used,
* A custom/standard conversion to string is used,
* if applicable; otherwise, some standard types can be
* converted by a lexical_cast (based on operator<< ).
* Otherwise, either the fallback string is used, or just
* a string denoting the (mangled) type.
* a string based on the (mangled) type.
*/
template<typename TY>
inline string
@ -109,24 +118,16 @@ namespace util {
, Symbol fallback =0 /// < replacement text to show if string conversion fails
)
{
if (can_ToString<TY>::value)
return string(prefix) + invoke_2string<TY>(val);
string res = _InvokeFailsafe<TY>::toString(val);
if (!isnil (res))
return string(prefix) + res;
else
{
if (can_lexical2string<TY>::value)
{
string res (invoke_indirect2string<TY> (val));
if ("" != res)
return string(prefix) + res;
}
return fallback? string(fallback)
: tyStr(val);
}
return fallback? string(fallback)
: tyStr(val);
}
/** @return a string denoting the type. */
template<typename TY>
inline string
@ -142,5 +143,4 @@ namespace util {
} // namespace util
#endif /*UTIL_FORMAT_H*/
#endif

View file

@ -30,7 +30,7 @@
** Because, if we can get a \c bool answer to such a question <i>at compile time,</i> we can use
** \c boost::enable_if to pick a special implementation based on the test result. Together, these
** techniques allow to adopt a duck-typed programming style, where an arbitrary object is allowed
** to enter a given API function, provided this object supports some special operations.
** to enter a given API function, provided this object supports some specific operations.
**
** While C++ certainly isn't a dynamic language and doesn't provide any kind of run time introspection,
** doing such check-and branch at compile time allows even to combine such a flexible approach with

149
src/lib/meta/size-trait.hpp Normal file
View file

@ -0,0 +1,149 @@
/*
SIZE-TRAIT.hpp - helpers and definitions to deal with the size of some known types
Copyright (C) Lumiera.org
2011, Hermann Vosseler <Ichthyostega@web.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef LIB_META_SIZE_TRAIT_H
#define LIB_META_SIZE_TRAIT_H
#include <vector>
#include <string>
namespace lib {
namespace meta {
/**
* A collection of constants to describe the expected size
* of some known classes, without needing to include the
* respective headers. This is an optimisation to improve
* compilation times and/or reduce size of the generated
* object files in debug mode. To get those sizes computed
* in a fairly portable way, but without much overhead,
* we mimic the memory layout of "the real thing"
* with some reasonable simplifications:
* - the size of vectors doesn't really depend on the elements
* - our strings, streams and buffers use just simple chars
*
* \par Interface
* The purpose of this whole construction is to pull off
* some constants based on sizeof expressions:
* - the size of a string
* - the size of a vector
* - the size of a boost::format
*
* @warning this setup is quite fragile and directly relies
* on the implementation layout of the GNU STL and Boost.
* Whenever using this stuff, make sure to place an assertion
* somewhere down in the implementation level to check against
* the size of the real thing.
*/
class SizeTrait
{
//-------------------------------------mimicked-definitions--
typedef std::vector<size_t> Vector;
typedef std::vector<bool> BVector;
struct CompatAllocator
: std::allocator<char>
{ };
struct Locale
{
void* _M_impl;
};
template<class T>
struct Optional
{
bool m_initialized_;
T m_storage_;
};
enum IOS_Openmode
{
_S_app = 1L << 0,
_S_ate = 1L << 1,
_S_bin = 1L << 2,
_S_in = 1L << 3,
_S_out = 1L << 4,
_S_trunc = 1L << 5,
_S_ios_openmode_end = 1L << 16
};
struct BasicStringbuf
{
char * _M_in_beg;
char * _M_in_cur;
char * _M_in_end;
char * _M_out_beg;
char * _M_out_cur;
char * _M_out_end;
Locale _M_buf_locale;
virtual ~BasicStringbuf() { }
};
struct BasicAltstringbuf
: BasicStringbuf
{
char * putend_;
bool is_allocated_;
IOS_Openmode mode_;
CompatAllocator alloc_;
};
struct BoostFormat
{
Vector items_;
BVector bound_; // note: differs in size
int style_;
int cur_arg_;
int num_args_;
mutable bool dumped_;
std::string prefix_;
unsigned char exceptions;
BasicAltstringbuf buf_;
Optional<Locale> loc_;
};
//-------------------------------------mimicked-definitions--
public:/* ===== Interface: size constants ===== */
enum { ALIGNMENT = sizeof(size_t)
, STRING = sizeof(std::string)
, VECTOR = sizeof(Vector)
, BVECTOR = sizeof(BVector)
, BOOST_FORMAT = sizeof(BoostFormat)
};
};
}} // namespace lib::meta
#endif

View file

@ -33,7 +33,7 @@
#include <boost/type_traits/remove_reference.hpp>
#include <boost/type_traits/remove_pointer.hpp>
#include <boost/type_traits/remove_cv.hpp>
#include <boost/utility/enable_if.hpp>
#include <string>
//Forward declarations for the Unwrap helper....
@ -42,14 +42,14 @@ namespace boost{
}
namespace std {
namespace tr1 {
template<class X> class reference_wrapper;
template<class X> class reference_wrapper;
template<class X> class shared_ptr;
}}
namespace lib{
template<class X, class B> class P;
template<class X, class B> class P;
}
namespace mobject{
template<class X, class B> class Placement;
template<class X, class B> class Placement;
}

View file

@ -24,7 +24,7 @@
/** @file typelist-util.hpp
** Metaprogramming: simple helpers for working with lists-of-types.
** This header provides some very basic "meta functions" for extracting
** some informations from a list-of-types. In Lumiera, we use template
** pieces of information from a list-of-types. In Lumiera, we use template
** metaprogramming and especially such lists-of-types, whenever we build
** some common implementation backbone, without being able to subsume all
** participating types (classes) into a single inheritance hierarchy.

View file

@ -21,10 +21,22 @@
*/
/** @file util.hpp
** Simple and lightweight helpers for metaprogramming and type detection.
** This header is a collection of very basic type detection and metaprogramming utilities.
** @warning indirectly, this header gets included into the majority of compilation units.
** Avoid anything here which increases compilation times or adds much debugging info.
**
** @see MetaUtils_test
** @see trait.hpp
** @see typelist.hpp
**
*/
#ifndef LIB_META_UTIL_H
#define LIB_META_UTIL_H
namespace lib {
namespace meta {
@ -33,7 +45,7 @@ namespace meta {
/* types for figuring out the overload resolution chosen by the compiler */
typedef char Yes_t;
struct No_t { char padding[8]; };
struct No_t { char more_than_one[4]; };
@ -56,6 +68,55 @@ namespace meta {
};
/** detect possibility of a conversion to string.
* Naive implementation just trying a the direct conversion.
* The embedded constant #value will be true in case this succeeds.
* Might fail in more tricky situations (references, const, volatile)
* @see string-util.hpp more elaborate solution including lexical_cast
*/
template<typename T>
struct can_convertToString
{
static T & probe();
static Yes_t check(std::string);
static No_t check(...);
public:
static const bool value = (sizeof(Yes_t)==sizeof(check(probe())));
};
/** strip const from type: naive implementation */
template<typename T>
struct UnConst
{
typedef T Type;
};
template<typename T>
struct UnConst<const T>
{
typedef T Type;
};
template<typename T>
struct UnConst<const T *>
{
typedef T* Type;
};
template<typename T>
struct UnConst<T * const>
{
typedef T* Type;
};
template<typename T>
struct UnConst<const T * const>
{
typedef T* Type;
};
/** semi-automatic detection if an instantiation is possible.
* Requires help by the template to be tested, which needs to define
* a typedef member \c is_defined. The embedded metafunction Test can be used

View file

@ -0,0 +1,104 @@
/*
NOBUG-RESOURCE-HANDLE-CONTEXT.hpp - thread local stack to manage NoBug resource handles
Copyright (C) Lumiera.org
2010, Hermann Vosseler <Ichthyostega@web.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/** @file nobug-resource-handle-context.hpp
** Thread-local stack of NoBug resource handles.
** This helper allows to access the resource handle in the nearest enclosing scope.
** The motivation for this approach was to avoid passing the resource handle over
** several intermediary function calls when using a scoped variable to control
** object monitor locking. Within this usage context, the necessity of passing
** a NoBug resource handle seems to be a cross-cutting concern, and not directly
** related to the core concern (controlling a mutex).
**
** @remarks as of 8/2011, this feature is not used anymore. In 12/2011, the concept
** of a diagnostic context stack was generalised. At that point, this header was
** created as an off-spin, now based on the generalised feature, to document this
** usage possibility, which might be required again at some point in the future.
**
** @see lib::DiagnosticContext
** @see diagnostic-context-test.hpp
**
**/
#ifndef LIB_NOBUG_RESOURCE_HANDLE_CONTEXT_H
#define LIB_NOBUG_RESOURCE_HANDLE_CONTEXT_H
#include "lib/error.hpp"
#include "lib/thread-local.hpp"
#include <boost/noncopyable.hpp>
#include <nobug.h>
namespace lib {
#ifdef NOBUG_MODE_ALPHA ////////////////////////TODO don't we need the handle in BETA builds for resource logging?
/**
* Diagnostic data frame to hold a NoBug resource handle.
* This way, code in nested function calls may pick up the nearest available handle.
* @warning relies on thread-local access; never use this within global data structures.
*/
class NobugResourceHandleContext
: DiagnosticContext<nobug_resource_user*>
{
public:
};
#else /* not NOBUG_ALPHA */
/**
* Disabled placeholder for the Diagnostic context, not used in release builds.
*/
class NobugResourceHandleContext
: boost::noncopyable
{
typedef nobug_resource_user* Handle;
public:
operator Handle () ///< payload: NoBug resource tracker user handle
{
return 0;
}
/** accessing the innermost diagnostic context created */
static NobugResourceHandleContext&
access ()
{
UNIMPLEMENTED ("how to disable DiagnosticContext with minimum overhead");
}
};
#endif /* NOBUG_ALPHA? */
} // namespace lib
#endif

View file

@ -73,6 +73,7 @@
#include "lib/util.hpp"
#include <boost/noncopyable.hpp>
#include <boost/static_assert.hpp>
namespace lib {
@ -266,7 +267,8 @@ namespace lib {
explicit
Buff (SUB const& obj)
{
REQUIRE (siz >= sizeof(SUB));
BOOST_STATIC_ASSERT (siz >= sizeof(SUB));
new(Buffer::ptr()) SUB (obj);
}
@ -613,6 +615,8 @@ namespace lib {
void
placeDefault()
{
BOOST_STATIC_ASSERT (siz >= sizeof(DEFAULT));
new(&buf_) DEFAULT();
}
@ -640,13 +644,14 @@ namespace lib {
destroy(); \
try \
{ \
REQUIRE (siz >= sizeof(TY)); \
return *new(&buf_) _CTOR_CALL_; \
} \
catch (...) \
{ \
placeDefault(); \
throw; \
BOOST_STATIC_ASSERT (siz >= sizeof(TY));\
\
return *new(&buf_) _CTOR_CALL_; \
} \
catch (...) \
{ \
placeDefault(); \
throw; \
}

View file

@ -146,6 +146,7 @@
#include "lib/meta/duck-detector.hpp"
#include <boost/utility/enable_if.hpp>
#include <boost/static_assert.hpp>
namespace lib {
@ -384,7 +385,8 @@ namespace lib {
template<class IMP>
PolymorphicValue (IMP*)
{
REQUIRE (siz >= sizeof(IMP));
BOOST_STATIC_ASSERT (siz >= sizeof(IMP));
new(&buf_) IMP();
}

View file

@ -49,7 +49,7 @@
#include "lib/error.hpp"
#include "lib/meta/generator.hpp"
#include "lib/meta/typelist-util.hpp"
#include "lib/format.hpp"
#include "lib/format-util.hpp"
#include "lib/typed-counter.hpp"
#include "include/logging.h"

View file

@ -46,7 +46,7 @@
#ifndef LIB_SUB_ID_H
#define LIB_SUB_ID_H
#include "lib/format.hpp"
#include "lib/format-util.hpp"
//#include <functional>
#include <boost/functional/hash.hpp> /////TODO better push the hash implementation into a cpp file (and btw, do it more seriously!)

View file

@ -95,7 +95,7 @@ namespace lib {
private:
TAR*
accessChecked()
accessChecked() const
{
TAR *p(get());
if (!p)

View file

@ -67,7 +67,7 @@
#define LIB_TYPED_ALLOCATION_MANAGER_H
#include "lib/error.hpp"
#include "lib/format.hpp"
#include "lib/format-util.hpp"
#include "lib/typed-counter.hpp"
#include "include/logging.h"

View file

@ -22,7 +22,7 @@
/** @file buildinstruct.hpp
** Helper classes used by asset::ProcPatt to represent the processing informations.
** Helper classes used by asset::ProcPatt to represent the processing information.
** Consider these classes as owned by ProcPatt. Non-inline functions go to procpatt.cpp
**
*/

View file

@ -24,7 +24,7 @@
/** @file struct-factory-impl.hpp
** Private implementation details of creating various structural assets.
** Details how to fabricate specific kinds of structural assets, based
** on capability informations encoded into a query (goal).
** on capability information encoded into a query (goal).
** @internal to be used by struct.cpp
**
** @see ConfigQuery

View file

@ -25,10 +25,10 @@
** A closure enabling self-contained execution of commands within the ProcDispatcher.
** After defining a proc-layer command, at some point the function arguments
** of the contained operation are "closed" by storing concrete argument values.
** These values will later on be fed to the operation when the command is invoked.
** These values will be fed later on to the operation when the command is invoked.
**
** Most of the command machinery accesses this function closure through the interface
** CmdClosure, while, when defining a command, subclasses typed to the specific
** Most of the command machinery accesses this function closure through the generic
** interface CmdClosure, while, when defining a command, subclasses typed to the specific
** function arguments are created. Especially, there is an ArgumentHolder template,
** which is used to define the storage for the concrete arguments. This ArgumentHolder
** internally contains an Closure<SIG> instance (where SIG is the signature of the
@ -43,10 +43,10 @@
**
** Later on, any command needs to be made ready for execution by binding it to a specific
** execution environment, which especially includes the target objects to be mutated by the
** command. Effectively, this means "closing" the Mutation (and UNDO) functor(s) with the
** command. Effectively, this means "closing" the Mutation (and UNDO) functor(s)) with the
** actual function arguments. These arguments are stored embedded within an ArgumentHolder,
** which thereby acts as closure. Besides, the ArgumentHolder also has to accommodate for
** storage holding the captured UNDO state (memento). Thus, internally the ArgumentHolder
** storage holding the captured UNDO state (memento). Internally the ArgumentHolder
** has to keep track of the actual types, thus allowing to re-construct the concrete
** function signature when closing the Mutation.
**
@ -74,7 +74,7 @@
#include "lib/meta/function-erasure.hpp"
#include "lib/meta/tuple.hpp"
#include "lib/meta/maybe-compare.hpp"
#include "lib/format.hpp"
#include "lib/format-util.hpp"
#include "lib/util.hpp"
#include "proc/control/argument-erasure.hpp"
#include "lib/typed-allocation-manager.hpp"
@ -214,7 +214,7 @@ namespace control {
friend bool
compare (ParamAccessor const&, ParamAccessor const&)
{
return true;;
return true;
}
};

View file

@ -31,7 +31,7 @@
**
** For actually providing these operations, the client is expected to call the
** definition functions in a chained manner ("fluent interface"). When finally all the
** required informations are available, a \em prototype object is built and registered
** required information is available, a \em prototype object is built and registered
** with the CommandRegistry. From this point on, the corresponding Command (frontend object)
** can be accessed directly by ID (and only relying on the header command.hpp).
**

View file

@ -60,7 +60,6 @@
#include "lib/error.hpp"
#include "lib/singleton.hpp"
#include "lib/sync.hpp"
#include "lib/format.hpp"
#include "include/logging.h"
#include "lib/util.hpp"

View file

@ -45,7 +45,7 @@
#include "lib/meta/function-closure.hpp"
#include "proc/control/command-signature.hpp"
#include "lib/functor-util.hpp"
#include "lib/format.hpp"
#include "lib/format-util.hpp"
#include "lib/util.hpp"
#include <boost/operators.hpp>

View file

@ -25,9 +25,9 @@
** During the Builder run, the render nodes network is wired up starting from the
** source (generating) nodes up to the exit nodes. As the wiring is implemented through
** a const engine::WiringDescriptor, when a new node gets fabricated, all of the connections
** to its predecessors need to be completely settled; similarly, any informations pertaining
** to its predecessors need to be completely settled; similarly, any information pertaining
** the desired operation mode of this node need to be available. Thus we use this temporary
** information record to assemble all these informations.
** information record to assemble all these pieces of information.
**
** @see proc::engine::NodeFactory
** @see nodewiring.hpp
@ -109,7 +109,7 @@ namespace engine {
/** A default WiringSituation doesn't specify any connections.
* It can be used as-is for building a source node, or augmented
* with connection informations later on.
* with connection information later on.
*/
WiringSituation()
: flags_(0)

View file

@ -103,10 +103,12 @@ namespace mobject {
};
private:
enum {
SPEC_SIZ = mp::maxSize<
enum
{ VTABLE = sizeof(size_t)
, SPEC_SIZ = VTABLE
+ mp::maxSize<
mp::Types< PID, lumiera_uid, uint>::List>::value
};
};
typedef lib::OpaqueHolder<TargetSpec, SPEC_SIZ> SpecBuff;

View file

@ -329,6 +329,22 @@ return: 0
END
TEST "formatting by string template" FormatString_test <<END
out-lit: --format-template--int=0012--double=+1.23--string=Lumiera --
out: 0x....+ _____ .
out: «.+util.+test.+Silent.»
out-lit: __nix_
out-lit: ____
out-lit: __1__
out-lit: __1__
out-lit: __↯__
out-lit: __dirt__
out-lit: __1234__
out-lit: __0xff__
return: 0
END
TEST "functional closure" FunctionClosure_test <<END
out: List1 :-<1>-<2>-<3>-
out: List2 :-<5>-<6>-<7>-
@ -389,6 +405,11 @@ return: 0
END
TEST "metaprogramming helpers" MetaUtils_test <<END
return: 0
END
TEST "NIL object singleton" NullValue_test <<END
return: 0
END
@ -648,6 +669,16 @@ return: 0
END
TEST "Wrapper thread-local pointers" ThreadLocal_test <<END
return: 0
END
TEST "Thread-local diagnostic context" DiagrnsticContext_test <<END
return: 0
END
TEST "Wait/Notify on Object Monitor" SyncWaiting_test <<END
return: 0
END

View file

@ -23,7 +23,7 @@
/** @file mediaacessmock.cpp
** Mock implementation of the Interface normally used to query media file
** informations from the data backend. The Mock implementation instead holds
** information from the data backend. The Mock implementation instead holds
** a map of fixed response which will be delivered when querying some magic
** filenames.
**

View file

@ -25,7 +25,7 @@
#include "lib/test/test-helper.hpp"
#include "proc/control/command-invocation.hpp"
#include "proc/control/command-def.hpp"
#include "lib/format.hpp"
#include "lib/format-util.hpp"
#include "lib/util.hpp"
#include "proc/control/test-dummy-commands.hpp"

View file

@ -0,0 +1,213 @@
/*
DiagrnsticContext(Test) - verify thread local stack for collecting diagnostics
Copyright (C) Lumiera.org
2011, Hermann Vosseler <Ichthyostega@web.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* *****************************************************/
#include "lib/test/run.hpp"
#include "lib/test/test-helper.hpp"
#include "backend/thread-wrapper.hpp"
#include "lib/diagnostic-context.hpp"
#include <cstdlib>
#include <vector>
namespace lib {
namespace test{
namespace error = lumiera::error;
namespace { // private test setup...
const uint NUM_THREADS = 50;
const uint MAX_RAND = 1000*1000;
inline bool
isOdd (uint val)
{
return bool (val % 2);
}
} // (End) test setup....
using backend::ThreadJoinable;
using error::LUMIERA_ERROR_LOGIC;
using std::rand;
/**
* Subject of this test:
* a thread-local stack of int values
*/
typedef DiagnosticContext<uint> Marker;
typedef std::vector<uint> VecI;
/**********************************************************************************
* @test verify a diagnostic facility to collect and access contextual information.
* DiagnosticContext frames are placed into automatic storage (as local
* variable within some function scope). Based on thread-local storage,
* the next reachable frame can be accessed from anywhere within
* the callstack. This feature is useful for collecting
* information regarding features cross-cutting
* the usual dependency hierarchy.
*
* @see lib::DiagnosticContext
* @see lib::ThreadLocal
* @see backend::Thread
*/
class DiagrnsticContext_test : public Test
{
virtual void
run (Arg)
{
verify_simpleAccess();
verify_heavilyParallelUsage();
}
/** @test create nested scopes and place a
* DiagnosticContext frame into each.
* Access the next reachable frame.
*/
void
verify_simpleAccess()
{
VERIFY_ERROR (LOGIC, Marker::access());
VecI loggedValues;
Marker zero(0);
CHECK (0 == zero);
CHECK (0 == Marker::access());
{ // nested scope
CHECK (0 == Marker::access());
Marker one(1);
CHECK (1 == Marker::access());
CHECK (1 == one);
CHECK (0 == zero);
{ // nested scope
CHECK (1 == Marker::access());
Marker two(2);
CHECK (2 == Marker::access());
CHECK (2 == two);
CHECK (1 == one);
CHECK (0 == zero);
loggedValues = Marker::extractStack();
}
CHECK (1 == Marker::access());
}
CHECK (0 == Marker::access());
CHECK (3 == loggedValues.size());
CHECK (2 == loggedValues[0]);
CHECK (1 == loggedValues[1]);
CHECK (0 == loggedValues[2]);
}
/** @test verify the thread local property of ContextStack.
* Create several threads, each of which processes a sequence
* of numbers starting from a random initial value down to one.
* Whenever a simple division by two leads to an odd value, this
* value is placed onto the ContextStack. At recursion end, we
* take a snapshot of the full ContextStack and then unwind.
* Thus the captured numbers must from a decreasing sequence
* of odd values.
*/
void
verify_heavilyParallelUsage()
{
TestThread testcase[NUM_THREADS] SIDEEFFECT;
for (uint i=0; i < NUM_THREADS; ++i)
CHECK (testcase[i].join().isValid() );
}
struct TestThread
: ThreadJoinable
{
TestThread()
: ThreadJoinable("test diagnostic context stack"
,verifyDiagnosticStack)
{ }
};
/** the actual test operation running in a separate thread */
static void
verifyDiagnosticStack()
{
uint seed (1 + rand() % MAX_RAND);
VecI sequence = descend (seed);
uint prev = 0;
for (uint i=0; i < sequence.size(); ++i)
{
uint val = sequence[i];
if (! (isOdd(val) && seed >= val && val > prev ))
throw error::Fatal ("thread-local diagnostic stack");
prev = val;
}
}
static VecI
descend (uint current)
{
if (current < 2)
return Marker::extractStack();
usleep (500);
if (isOdd(current))
{
Marker remember(current);
return descend (current+1);
}
else
return descend (current/2);
}
};
/** Register this test class... */
LAUNCHER (DiagrnsticContext_test, "function common");
}} // namespace backend::test

View file

@ -22,7 +22,7 @@
#include "lib/test/run.hpp"
#include "lib/format.hpp"
#include "lib/format-util.hpp"
#include "lib/error.hpp"
#include <iostream>
@ -35,8 +35,7 @@ namespace util {
namespace test {
class Reticent
{
};
{ };
class UnReticent
: public Reticent
@ -47,10 +46,11 @@ namespace test {
/*************************************************
* verifies the proper working of helper functions
* frequently used within the Lumiera testsuite.
* @see test-helper.hpp
/*******************************************************************************
* @test verifies the proper working of some string-formatting helper functions.
* - util::str() provides a failsafe to-String conversion, preferring
* an built-in conversion, falling back to just a mangled type string.
* @see format-util.hpp
*/
class FormatHelper_test : public Test
{
@ -61,7 +61,7 @@ namespace test {
}
/** @test verify the maybe-to-string conversion. */
/** @test verify a failasfe to-string conversion. */
void
check2String ()
{

View file

@ -0,0 +1,326 @@
/*
FormatString(Test) - validate string template formatting (printf style)
Copyright (C) Lumiera.org
2011, Hermann Vosseler <Ichthyostega@web.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* *****************************************************/
#include "lib/test/run.hpp"
#include "lib/test/test-helper.hpp"
#include "lib/format-string.hpp"
#include "lib/error.hpp"
#include "lib/util.hpp"
#include <boost/lexical_cast.hpp>
#include <iostream>
#include <cstdlib>
using boost::lexical_cast;
using std::cout;
using std::endl;
namespace util {
namespace test {
namespace error = lumiera::error;
/*******************************************************************************
* @test verifies our front-end for printf-style format string based formatting.
* - the implementation is based on boost::format
* - we create a wrapper-object on-the fly, which is able to hold
* the result of a partial invocation, until all parameters are given.
* - explicit specialisations feed all primitive types directly down
* into the implementation level. If possible, a custom operator string()
* will be used for formatting.
* - exceptions will be logged, but handled gracefully
*
* @see format-string.hpp
* @see boost::format
*/
class FormatString_test : public Test
{
void
run (Arg)
{
check_simpleInvocation();
check_delayedInvocation();
verify_typeHandling();
verify_customType();
verify_errorHandling();
verify_pointerHandling();
}
void
check_simpleInvocation ()
{
string formatted = _Fmt("--format-template--int=%04d--double=%+5.2f--string=%-9s--")
% 12
% 1.228
% "Lumiera";
cout << formatted << endl;
CHECK (formatted == "--format-template--int=0012--double=+1.23--string=Lumiera --");
}
void
check_delayedInvocation ()
{
string formatString = "%p %|20T_| %u";
_Fmt formatter (formatString);
uint val = rand() % 100;
void *pt = &val;
formatter % pt;
formatter % val;
cout << formatter << endl;
}
void
verify_typeHandling ()
{
int i(-12);
CHECK (_Fmt("%d") % i == "-12" );
CHECK (_Fmt("%6d") % i == " -12" );
CHECK (_Fmt("%-6d") % i == "-12 " );
CHECK (_Fmt("%+-6d") % -i == "+12 " );
CHECK (_Fmt("%+06d") % -i == "+00012" );
CHECK (_Fmt("%06X") % -i == "00000C" );
CHECK (_Fmt("%#x") % -i == "0xc" );
uint u(12);
CHECK (_Fmt("%d") % u == "12" );
CHECK (_Fmt("%6d") % u == " 12" );
CHECK (_Fmt("%-6d") % u == "12 " );
CHECK (_Fmt("%-+6d") % u == "12 " );
CHECK (_Fmt("%+06d") % u == "000012" );
short sh(-123);
CHECK (_Fmt("%6d") % sh == " -123" );
ushort ush(123);
CHECK (_Fmt("%6d") % ush == " 123" );
long l(-123);
CHECK (_Fmt("%6d") % l == " -123" );
ulong ul(123);
CHECK (_Fmt("%6d") % ul == " 123" );
int64_t ll(5e+9);
CHECK (_Fmt("%d") % ll == "5000000000" );
CHECK (_Fmt("%5.3d") %ll == "5000000000" );
CHECK (_Fmt("%10.3e") %ll == "5000000000" );
uint64_t ull(ll);
CHECK (_Fmt("%d") % ull == "5000000000" );
float f(12.34);
CHECK (_Fmt("%g") % f == "12.34" );
CHECK (_Fmt("%d") % f == "12.34" );
CHECK (_Fmt("%.3f") % f == "12.340" );
CHECK (_Fmt("%.1e") % f == "1.2e+01");
CHECK (_Fmt("%.0f") % f == "12" );
double d(-12.34);
CHECK (_Fmt("%g") % d == "-12.34" );
char c(0x40);
CHECK (_Fmt("%d") % c == "@" );
CHECK (_Fmt("%x") % c == "@" );
CHECK (_Fmt("%o") % c == "@" );
CHECK (_Fmt("%c") % c == "@" );
CHECK (_Fmt("%s") % c == "@" );
CHECK (_Fmt("%d") % short(c) == "64" );
CHECK (_Fmt("%x") % short(c) == "40" );
CHECK (_Fmt("%o") % short(c) == "100" );
CHECK (_Fmt("%c") % short(c) == "6" );
CHECK (_Fmt("%s") % short(c) == "64" );
uchar uc(0xff);
CHECK (_Fmt("%d") % uint(uc) == "255" );
CHECK (_Fmt("%x") % uint(uc) == "ff" );
CHECK (_Fmt("%X") % uint(uc) == "FF" );
CHECK (_Fmt("%c") % uint(uc) == "2" );
CHECK (_Fmt("%s") % uint(uc) == "255" );
CHECK (_Fmt("%d") % uc == "\xFF");
CHECK (_Fmt("%x") % uc == "\xFF");
CHECK (_Fmt("%X") % uc == "\xFF");
CHECK (_Fmt("%c") % uc == "\xFF");
CHECK (_Fmt("%s") % uc == "\xFF");
c = char(uc);
CHECK (_Fmt("%c") % c == "\xFF");
string str("Lumiera");
CHECK (_Fmt("%s") % str == "Lumiera" );
CHECK (_Fmt("%10s") % str == " Lumiera" );
CHECK (_Fmt("%7.4s") %str == " Lumi" );
CHECK (_Fmt("%10c") % str == " L" );
}
/* == some custom types to test with == */
struct Silent
{
int i_;
Silent(int i) : i_(i) { }
};
struct Verbose
: Silent
{
Verbose(int i) : Silent(i) { }
virtual
operator string() const
{
return _Fmt("Number-%03d") % i_;
}
};
struct Explosive
: Verbose
{
Explosive(int i) : Verbose(i) { }
operator string() const
{
if (23 == i_)
throw error::State("encountered Fantomas");
else
return Verbose::operator string();
}
};
void
verify_customType ()
{
Silent s(12);
Verbose v(13);
Explosive x(23);
Silent& rs1 = v;
Silent& rs2 = x;
Verbose& rv = x;
string mangledType = _Fmt("%s") % s;
cout << mangledType << endl;
CHECK (contains (mangledType, "util"));
CHECK (contains (mangledType, "test"));
CHECK (contains (mangledType, "Silent"));
CHECK (_Fmt("!!%s!!") % v == "!!Number-013!!");
CHECK (_Fmt("!!%s!!") % x == "!!<string conversion failed: LUMIERA_ERROR_STATE:unforeseen state (encountered Fantomas).>!!");
CHECK (contains (_Fmt("%s") % rs1, "Silent"));
CHECK (contains (_Fmt("%s") % rs2, "Silent"));
CHECK (_Fmt("!!%s!!") % rv == "!!<string conversion failed: LUMIERA_ERROR_STATE:unforeseen state (encountered Fantomas).>!!");
x.i_ = 42;
CHECK (_Fmt("!!%s!!") % rv == "!!Number-042!!");
CHECK (_Fmt("!!%s!!") % x == "!!Number-042!!");
}
void
verify_errorHandling ()
{
cout << _Fmt("__nix_") % 1 % 2 << endl;
cout << _Fmt("__%d__") << endl;
cout << _Fmt("__%d__") % 1 << endl;
cout << _Fmt("__%d__") % 1 % 2 << endl;
const char* evil = NULL;
cout << _Fmt("__%d__") % evil << endl;
cout << _Fmt("__%d__") % "dirt" << endl;
cout << _Fmt("__%d__") % "1234" << endl;
cout << _Fmt("__%d__") % "0xff" << endl;
VERIFY_ERROR(FORMAT_SYNTAX, _Fmt("%broken"));
}
void
verify_pointerHandling ()
{
int i(-12); int * pi = & i;
uint u(12); uint * pu = & u;
short sh(-123); short * psh = & sh;
ushort ush(123); ushort * push = &ush;
long l(-123); long * pl = & l;
ulong ul(123); ulong * pul = & ul;
int64_t ll(5e+9); int64_t * pll = & ll;
uint64_t ull(ll); uint64_t * pull = &ull;
float f(12.34); float * pf = & f;
double d(-12.34); double * pd = & d;
uchar uc(0xff); uchar * puc = & uc;
string str("Lumiera"); string * pstr = &str;
CHECK (_Fmt("%d") % i == _Fmt("%d") % pi);
CHECK (_Fmt("%d") % u == _Fmt("%d") % pu);
CHECK (_Fmt("%d") % sh == _Fmt("%d") % psh);
CHECK (_Fmt("%d") % ush == _Fmt("%d") % push);
CHECK (_Fmt("%d") % l == _Fmt("%d") % pl);
CHECK (_Fmt("%d") % ul == _Fmt("%d") % pul);
CHECK (_Fmt("%d") % ll == _Fmt("%d") % pll);
CHECK (_Fmt("%d") % ull == _Fmt("%d") % pull);
CHECK (_Fmt("%e") % f == _Fmt("%e") % pf);
CHECK (_Fmt("%e") % d == _Fmt("%e") % pd);
CHECK (_Fmt("%g") % uc == _Fmt("%g") % puc);
CHECK (_Fmt("%s") % str == _Fmt("%s") % pstr);
char c('a');
char pc[2] = {'a', '\0'};
CHECK (_Fmt("%g") % c == _Fmt("%g") % pc);
Verbose v(42);
Verbose *pv = &v;
void *vv = pv;
CHECK (_Fmt("__%s__") % v == "__Number-042__");
CHECK (_Fmt("__%s__") % pv == "__Number-042__");
CHECK (_Fmt("__%s__") % vv != "__Number-042__");
string address = lexical_cast<string>(pv);
CHECK (_Fmt("__%s__") % vv == "__"+address+"__");
pv = NULL;
vv = NULL;
CHECK (_Fmt("__%s__") % pv == "__<null>__");
CHECK (_Fmt("__%s__") % vv == "__0__");
}
};
LAUNCHER (FormatString_test, "unit meta");
}} // namespace util::test

View file

@ -90,7 +90,7 @@ namespace test{
/** Register this test class... */
LAUNCHER (DuckDetector_test, "unit common");
LAUNCHER (DuckDetector_test, "unit meta");

View file

@ -0,0 +1,171 @@
/*
MetaUtils(Test) - check some simple type trait helpers
Copyright (C) Lumiera.org
2011, Hermann Vosseler <Ichthyostega@web.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* *****************************************************/
#include "lib/test/run.hpp"
#include "lib/meta/util.hpp"
#include "lib/meta/typelist.hpp"
#include <string>
namespace lib {
namespace meta {
namespace test {
using std::string;
/*************************************************************************
* @test verify basic type trait and metaprogramming helpers.
* - marker types to tell which overload the compiler picks
* - simple trait to detect the possibility of a string conversion
* - trait to detect a typelist type
*/
class MetaUtils_test : public Test
{
void
run (Arg)
{
verify_basicAssumptions();
detect_stringConversion();
detect_typeList();
}
/** @test demonstrate / verify the
* basic type trait detection technique:
* By investigating the return type, we can
* figure out which overload the compiler picks..
*/
void
verify_basicAssumptions()
{
CHECK (sizeof(Yes_t) != sizeof (No_t));
CHECK (sizeof(Yes_t) == sizeof (probe (1)));
CHECK (sizeof(Yes_t) == sizeof (probe (1L))); // conversion long -> int
CHECK (sizeof(Yes_t) == sizeof (probe ('a'))); // conversion char -> int
CHECK (sizeof(No_t) == sizeof (probe ("a"))); // char * can't be converted
}
static Yes_t probe (int);
static No_t probe (...);
//-------------------------------------------------TEST-types--
class SubString : public string
{
public:
SubString() : string("sublunar") { }
};
class Something { };
struct SomehowStringy
{
operator string() { return "No such thing"; }
};
struct SomehowSubtle
{
operator SubString() { return SubString(); }
};
class SomehowSubSub : public SomehowSubtle { };
//-------------------------------------------------TEST-types--
template<typename TY>
static bool
can_convert (TY const&)
{
return can_convertToString<TY>::value;
}
void
detect_stringConversion()
{
CHECK ( can_convert (string("inline string")));
CHECK ( can_convert ("char literal"));
CHECK (!can_convert (23.34));
CHECK (!can_convert (23));
CHECK (!can_convert (1L));
string str("mhm");
string & str_ref (str);
string const& str_const_ref (str);
string * str_ptr = &str;
CHECK ( can_convert (str));
CHECK ( can_convert (str_ref));
CHECK ( can_convert (str_const_ref));
CHECK ( can_convert (*str_ptr));
CHECK (!can_convert (str_ptr));
SubString sub;
Something thing;
const SomehowStringy stringy = SomehowStringy();
SomehowSubSub subsub;
SubString const& subRef(subsub);
CHECK ( can_convert (sub));
CHECK (!can_convert (thing));
CHECK ( can_convert (stringy));
CHECK ( can_convert (subsub));
CHECK ( can_convert (subRef));
}
//-------------------------------------------------TEST-types--
typedef Types< int
, uint
, int64_t
, uint64_t
>::List TheList;
typedef Types< >::List EmptyList;
//-------------------------------------------------TEST-types--
void
detect_typeList()
{
CHECK ( is_Typelist<TheList>::value);
CHECK ( is_Typelist<EmptyList>::value);
CHECK (!is_Typelist<Something>::value);
}
};
/** Register this test class... */
LAUNCHER (MetaUtils_test, "unit meta");
}}} // namespace lib::meta::test

View file

@ -451,7 +451,7 @@ namespace test {
/** Register this test class... */
LAUNCHER (TypeTuple_test, "unit common");
LAUNCHER (TypeTuple_test, "unit meta");

View file

@ -76,7 +76,7 @@ namespace test {
/** Register this test class... */
LAUNCHER (TypeListUtil_test, "unit common");
LAUNCHER (TypeListUtil_test, "unit meta");

View file

@ -0,0 +1,113 @@
/*
ThreadLocal(Test) - verify wrapper for using thread-local data
Copyright (C) Lumiera.org
2011, Hermann Vosseler <Ichthyostega@web.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* *****************************************************/
#include "lib/test/run.hpp"
#include "backend/thread-wrapper.hpp"
#include "lib/thread-local.hpp"
#include <cstdlib>
using test::Test;
using backend::ThreadJoinable;
using std::rand;
namespace lib {
namespace test{
namespace { // private test setup...
const uint NUM_THREADS = 50;
const uint MAX_RAND = 5*1000*1000;
/** Subject of the test */
ThreadLocalPtr<uint> privateValue;
struct TestThread
: ThreadJoinable
{
TestThread()
: ThreadJoinable("test Thread-local storage"
,verifyThreadLocal)
{ }
/** the actual test operation running in a separate thread */
static void
verifyThreadLocal()
{
uint secret (1 + rand() % MAX_RAND);
privateValue.set (&secret);
usleep (secret); // sleep for a random period
if (secret != *privateValue)
throw error::Fatal ("thread-local value access broken");
}
};
} // (End) test setup....
/**************************************************************************
* @test use a wrapper to simplify handling of thread-local data.
* Create some threads, each referring to another piece of data
* through the "same" wrapper instance.
*
* @see backend::Thread
* @see lib::ThreadLocal
*/
class ThreadLocal_test : public Test
{
virtual void
run (Arg)
{
TestThread testcase[NUM_THREADS] SIDEEFFECT;
for (uint i=0; i < NUM_THREADS; ++i)
CHECK (testcase[i].join().isValid() );
}
};
/** Register this test class... */
LAUNCHER (ThreadLocal_test, "function common");
}} // namespace backend::test

View file

@ -88,7 +88,7 @@ namespace test{
FixedFrameQuantiser fixQ(25);
uint frames = (rand() % MAX_FRAMES);
FSecs dirt = (F25 / (1 + rand() % DIRT_GRAIN));
FSecs dirt = (F25 / (2 + rand() % DIRT_GRAIN));
Time rawTime (dirt + frames*F25);

View file

@ -747,10 +747,10 @@ Even if the low-level memory manager(s) may use raw storage, we require that the
&amp;rarr; see MemoryManagement
</pre>
</div>
<div title="Asset" modifier="Ichthyostega" modified="201012292209" created="200708100337" tags="def classes img" changecount="20">
<div title="Asset" modifier="Ichthyostega" modified="201112222243" created="200708100337" tags="def classes img" changecount="21">
<pre>Asset management is a subsystem on its own. Assets are &quot;things&quot; that can be loaded into a session, like Media, Clips, Effects, Transitions. It is the &quot;bookkeeping view&quot;, while the Objects in the Session relate to the &quot;manipulation and process view&quot;. Some Assets can be //loaded// and a collection of Assets is saved with each Session. Besides, there is a collection of basic Assets always available by default.
The Assets are important reference points holding the information needed to access external resources. For example, an Clip asset can reference a Media asset, which in turn holds the external filename from which to get the media stream. For Effects, the situation is similar. Assets thus serve two quite distinct purposes. One is to load, list, group search and browse them, and to provide an entry point to create new or get at existing MObject in the Session, while the other purpose is to provide attribute and property informations to the inner parts of the engine, while at the same time isolating and decoupling them from environmental details.
The Assets are important reference points holding the information needed to access external resources. For example, an Clip asset can reference a Media asset, which in turn holds the external filename from which to get the media stream. For Effects, the situation is similar. Assets thus serve two quite distinct purposes. One is to load, list, group search and browse them, and to provide an entry point to create new or get at existing MObject in the Session, while the other purpose is to provide attribute and property information to the inner parts of the engine, while at the same time isolating and decoupling them from environmental details.
We can distinguish several different Kinds of Assets, each one with specific properties. While all these Kinds of Assets implement the basic Asset interface, they in turn are the __key abstractions__ of the asset management view. Mostly, their interfaces will be used directly, because they are quite different in behaviour. Thus it is common to see asset related operations being templated on the Asset Kind.
&amp;rarr; see also [[Creating and registering Assets|AssetCreation]]
@ -759,7 +759,7 @@ We can distinguish several different Kinds of Assets, each one with specific pro
!Media Asset
Some piece of Media Data accessible at some external Location and able to be processed by Lumiera. A Media File on Harddisk can be considered as the most basic form of Media Asset, with some important derived flavours, like a Placeholder for a currently unavailable Source, or Media available in different Resolutions or Formats.
* __outward interface operations__ include querying properties, creating an Clip MObject, controlling processing policy (low res proxy placeholders, interlacing and other generic pre- and postprocessing)
* __inward interface operations__ include querying filename, codec, offset and any other informations necessary for creating a source render node, getting additional processing policy decisions (handling of interlacing, aspect ratio).
* __inward interface operations__ include querying filename, codec, offset and any other information necessary for creating a source render node, getting additional processing policy decisions (handling of interlacing, aspect ratio).
&amp;rarr; MediaAsset
!Processing Asset
@ -1424,8 +1424,8 @@ Commands are //defined// using a [[fluent API|http://en.wikipedia.org/wiki/Fluen
&amp;rarr; see CommandUsage
</pre>
</div>
<div title="CommandDefinition" modifier="Ichthyostega" modified="200907220310" created="200906140124" tags="SessionLogic spec draft decision design" changecount="10">
<pre>Commands can be identified and accessed //by name// &amp;mdash; consequently there needs to be an internal command registry, including a link to the actual implementing function, thus allowing to re-establish the connection between command and implementing functions when de-serialising a persisted command. To create a command, we need to provide the following informations
<div title="CommandDefinition" modifier="Ichthyostega" modified="201112222243" created="200906140124" tags="SessionLogic spec draft decision design" changecount="11">
<pre>Commands can be identified and accessed //by name// &amp;mdash; consequently there needs to be an internal command registry, including a link to the actual implementing function, thus allowing to re-establish the connection between command and implementing functions when de-serialising a persisted command. To create a command, we need to provide the following information
* operation function actually implementing the command
* function to [[undo|UndoManager]] the effect of the command
* function to capture state to be used by UNDO.
@ -1491,11 +1491,11 @@ To support this handling scheme, some infrastructure is in place:
* performing the actual execution is delegated to a handling pattern object, accessed by name.
</pre>
</div>
<div title="CommandLifecycle" modifier="Ichthyostega" modified="200910090043" created="200907210135" tags="SessionLogic spec draft design img" changecount="19">
<div title="CommandLifecycle" modifier="Ichthyostega" modified="201112222244" created="200907210135" tags="SessionLogic spec draft design img" changecount="20">
<pre>[&lt;img[Structure of Commands|uml/fig135173.png]]
While generally the command framework was designed to be flexible and allow a lot of different use cases, execution paths and to serve various goals, there is an ''intended lifecycle'' &amp;mdash; commands are expected to go through several distinct states.
The handling of a command starts out with a ''command ID'' provided by the client code. Command ~IDs are unique (human readable) identifiers and should be organised in a hierarchical fashion. When provided with an ID, the CommandRegistry tries to fetch an existing command definition. In case this fails, we enter the [[command definition stage|CommandDefinition]], which includes specifying functions to implement the operation, state capturing and UNDO. When all these informations are available, the entity is called a ''command definition''. Conceptually, it is comparable to a //class// or //meta object.//
The handling of a command starts out with a ''command ID'' provided by the client code. Command ~IDs are unique (human readable) identifiers and should be organised in a hierarchical fashion. When provided with an ID, the CommandRegistry tries to fetch an existing command definition. In case this fails, we enter the [[command definition stage|CommandDefinition]], which includes specifying functions to implement the operation, state capturing and UNDO. When all of this information is available, the entity is called a ''command definition''. Conceptually, it is comparable to a //class// or //meta object.//
By ''binding'' to specific operation arguments, the definition is //armed up//&amp;nbsp; and becomes a real ''command''. This is similar to creating an instance from a class. Behind the scenes, storage is allocated to hold the argument values and any state captured to create the ability to UNDO the command's effect later on.
@ -1801,7 +1801,7 @@ Cinelerra uses this term in a related manner but with a somewhat shifted focus:
In this usage, the EDL in most cases will be almost synonymous to &amp;raquo;the session&amp;laquo;, just the latter emphasizes more the state aspect. While the Lumiera project started out using the same terminology, later on, when support for multiple &quot;containers&quot; within the session and for [[meta-clips|VirtualClip]] was determined to be of much importance, the new term &amp;raquo;[[Sequence]]&amp;laquo; was preferred.
</pre>
</div>
<div title="EditingOperations" modifier="Ichthyostega" modified="200710111508" created="200709251610" tags="design decision" changecount="5">
<div title="EditingOperations" modifier="Ichthyostega" modified="201112222244" created="200709251610" tags="design decision" changecount="6">
<pre>These are the tools provided to any client of the Proc layer for handling and manipulating the entities in the Session. When defining such operations, //the goal should be to arrive at some uniformity in the way things are done.// Ideally, when writing client code, one should be able to guess how to achieve some desired result.
!guiding principle
@ -1825,7 +1825,7 @@ This has some obvious and some subtle consequences. Of course, manipulating //ta
Consequently, sometimes there needs to be done sort of a ''read-copy-update'', i.e. self-replacement by copy followed by manipulation of the new copy, while ongoing processes use the unaltered original object until they receive some sort of reset.
!!undo
Basically, each elementary operation has to record the informations necessary to be undone. It does so by registering a Memento with some central UndoManager facility. This Memento object contains a functor pre-bound with the needed parameter values. (Besides, the UndoManager is free to implement a second level of security by taking independent state snapshots).
Basically, each elementary operation has to record the information necessary to be undone. It does so by registering a Memento with some central UndoManager facility. This Memento object contains a functor pre-bound with the needed parameter values. (Besides, the UndoManager is free to implement a second level of security by taking independent state snapshots).
{{red{to be defined in more detail later...}}}
</pre>
</div>
@ -2813,8 +2813,8 @@ The ''pull up'' sequence performs basic initialisation of the session facilities
The ''shut down'' sequence does exactly that: halt processing and rendering, disconnect an existing session, if any, get back into initial state. It doesn't care for unwinding session contents, which is assumed to happen automatically when references to previous session contents go out of scope.
</pre>
</div>
<div title="LoadingMedia" modifier="Ichthyostega" modified="200806030203" created="200709220005" tags="design spec" changecount="4">
<pre>Opening and accessing media files on disk poses several problems, most of which belong to the domain of Lumiera's data backend. Here, we focus on the questions related to making media data available to the session and the render engine. Each media will be represented by an MediaAsset object, which indeed could be a compound object (in case of MultichannelMedia). Building this asset object thus includes getting informations from the real file on disk. For delegating this to the backend, we use the following query interface:
<div title="LoadingMedia" modifier="Ichthyostega" modified="201112222244" created="200709220005" tags="design spec" changecount="5">
<pre>Opening and accessing media files on disk poses several problems, most of which belong to the domain of Lumiera's data backend. Here, we focus on the questions related to making media data available to the session and the render engine. Each media will be represented by an MediaAsset object, which indeed could be a compound object (in case of MultichannelMedia). Building this asset object thus includes getting information from the real file on disk. For delegating this to the backend, we use the following query interface:
* {{{queryFile(char* name)}}} requests accessing the file and yields some (opaque) handle when successful.
* {{{queryChannel(fHandle, int)}}} will then be issued in sequence with ascending index numbers, until it returns {{{NULL}}}.
* the returned struct (pointer) will provide the following information:
@ -3294,15 +3294,15 @@ But because I know the opinions on this topc are varying (users tend to be delig
My proposed aproach is to treat OpenGL as a separate video raw data type, requiring separete and specialized [[Processing Nodes|ProcNode]] for all calculations. Thus the Builder could connect OpenGL nodes if it is possible to cover the render path in whole or partially or maybe even just for preview.
</pre>
</div>
<div title="OperationPoint" modifier="Ichthyostega" modified="201011071905" created="200805270334" tags="def impl Builder" changecount="9">
<div title="OperationPoint" modifier="Ichthyostega" modified="201112222245" created="200805270334" tags="def impl Builder" changecount="10">
<pre>A low-level abstraction within the [[Builder]] &amp;mdash; it serves to encapsulate the details of making multi-channel connections between the render nodes: In some cases, a node can handle N channels internally, while in other cases we need to replicate the node N times and wire each channel individually. As it stands, the OperationPoint marks the ''borderline between high-level and low-level model'': it is invoked in terms of ~MObjects and other entities of the high-level view, but internally it manages to create ProcNode and similar entities of the low-level model.
The operation point is provided by the current BuilderMould and used by the [[processing pattern|ProcPatt]] executing within this mould and conducting the current build step. The operation point's interface allows //to abstract//&amp;nbsp; these details, as well as to //gain additional control//&amp;nbsp; if necessary (e.g. addressing only one of the channels). The most prominent build instruction used within the processing patterns (which is the instruction {{{&quot;attach&quot;}}}) relies on the aforementioned //approach of abstracted handling,// letting the operation point determine automatically how to make the connection.
This is possible because the operation point has been provided (by the mould) with informations about the media stream type to be wired, which, together with information accessible at the [[render node interface|ProcNode]] and from the [[referred processing assets|ProcAsset]], with the help of the [[connection manager|ConManager]] allows to figure out what's possible and how to do the desired connections. Additionally, in the course of deciding about possible connections, the PathManager is consulted to guide strategic decisions regarding the [[render node configuration|NodeConfiguration]], possible type conversions and the rendering technology to employ.
This is possible because the operation point has been provided (by the mould) with information about the media stream type to be wired, which, together with information accessible at the [[render node interface|ProcNode]] and from the [[referred processing assets|ProcAsset]], with the help of the [[connection manager|ConManager]] allows to figure out what's possible and how to do the desired connections. Additionally, in the course of deciding about possible connections, the PathManager is consulted to guide strategic decisions regarding the [[render node configuration|NodeConfiguration]], possible type conversions and the rendering technology to employ.
</pre>
</div>
<div title="OutputDesignation" modifier="Ichthyostega" modified="201106162303" created="201006220126" tags="Model draft design discuss" changecount="59">
<div title="OutputDesignation" modifier="Ichthyostega" modified="201112222245" created="201006220126" tags="Model draft design discuss" changecount="60">
<pre>An ever recurring problem in the design of Luimiera's ~Proc-Layer is how to refer to output destinations, and how to organise them.
Wiring the flexible interconnections between the [[pipes|Pipe]] should take into account both the StreamType and the specific usage context ([[scope|PlacementScope]]) -- and the challenge is to avoid hard-linking of connections and tangling with the specifics of the target to be addressed and connected. This page, started __6/2010__ by collecting observations to work out the relations, arrives at defining a //key abstraction// of output management.
@ -3314,7 +3314,7 @@ Wiring the flexible interconnections between the [[pipes|Pipe]] should take into
** as Timeline is just a façade, BindingMO has to expose something which can be referred for attaching effects (to global pipes)
** when used as VirtualClip, there is somehow a channel configuration, either as asset, or exposed by the BindingMO
* Placements always resolve at least two dimensions: time and output. The latter means that a [[Placement]] can figure out to where to connect
* the resolution ability of Placements could help to overcome the problems in conjunction with a VirtualClip: missing output destination informations could be inherited down....
* the resolution ability of Placements could help to overcome the problems in conjunction with a VirtualClip: missing output destination information could be inherited down....
* expanding on the basic concept of a Placement in N-dimensional configuration space, this //figuring out// would denote the ability to resolve the final output destination
* this resolution to a final destination is explicitly context dependent. We engage into quite some complexities to make this happen (&amp;rarr; BindingScopeProblem)
* [[processing patterns|ProcPatt]] are used for creating nodes on the source network of a clip, and similarly for fader, overlay and mixing into a summation pipe
@ -3454,7 +3454,7 @@ Thus the mapping is a copyable value object, using an associative array. It may
First and foremost, mapping can be seen as a //functional abstraction.// As it's used at implementation level, encapsulation of detail types in't the primary concern, so it's a candidate for generic programming: For each of those use cases outlined above, a distinct mapping type is created by instantiating the {{{OutputMapping&lt;DEF&gt;}}} template with a specifically tailored definition context ({{{DEF}}}), which takes on the role of a strategy. Individual instances of this concrete mapping type may be default created and copied freely. This instantiation process includes picking up the concrete result type and building a functor object for resolving on the fly. Thus, in the way typical for generic programming, the more involved special details are moved out of sight, while being still in scope for the purpose of inlining. But there //is// a concern better to be encapsulated and concealed at the usage site, namely accessing the rules system. Thus mapping leads itself to the frequently used implementation pattern where there is a generic frontend as header, calling into opaque functions embedded within a separate compilation unit.
</pre>
</div>
<div title="OutputSlot" modifier="Ichthyostega" modified="201112222235" created="201106162339" tags="def Concepts Player spec img" changecount="51">
<div title="OutputSlot" modifier="Ichthyostega" modified="201112230113" created="201106162339" tags="def Concepts Player spec img" changecount="52">
<pre>Within the Lumiera player and output subsystem, actually sending data to an external output requires to allocate an ''output slot''
This is the central metaphor for the organisation of actual (system level) outputs; using this concept allows to separate and abstract the data calculation and the organisation of playback and rendering from the specifics of the actual output sink. Actual output possibilities (video in GUI window, video fullscreen, sound, Jack, rendering to file) can be added and removed dynamically from various components (backend, GUI), all using the same resolution and mapping mechanisms (&amp;rarr; OutputManagement)
@ -3464,7 +3464,7 @@ Each OutputSlot is an unique and distinguishable entity. It corresponds explicit
In order to be usable as //output sink,// an output slot needs to be //allocated,// i.e. tied to and locked for a specific client. At any time, there may be only a single client using a given output slot this way. To stress this point: output slots don't provide any kind of inherent mixing capability; any adaptation, mixing, overlaying and sharing needs to be done within the nodes network producing the output data fed to the slot. (in special cases, some external output capabilities -- e.g. the Jack audio connection system -- may still provide additional mixing capabilities, but that's beyond the scope of the Lumiera application)
[&gt;img[Outputslot implementation structures|uml/fig151685.png]]
Once allocated, the output slot returns a set of concrete ''sink handles'' (one for each physical channel expecting data). The calculating process feeds its results into those handles. Size and other characteristics of the data frames are assumed to be suitable, which typically won't be verified at that level anymore (but the sink handle provides a hook for assertions). Besides that, the allocation of an output slot reveals detailed ''timing expectations''. The client is required to comply to these timings when ''emitting'' data -- he's even required to provide a //current time specification,// alongside with the data. Yet the output slot has the ability to handle timing failures gracefully; the concrete output slot implementation is expected to provide some kind of de-click or de-flicker facility, which kicks in automatically when a timing failure is detected.
Once allocated, the output slot returns a set of concrete ''sink handles'' (one for each physical channel expecting data). The calculating process feeds its results into those handles. Size and other characteristics of the data frames are assumed to be suitable, which typically won't be verified at that level anymore (but the sink handle provides a hook for assertions). Besides that, the allocation of an output slot reveals detailed ''timing expectations''. The client is required to comply to these timings when ''emitting'' data -- he's even required to provide a //current time specification,// alongside with the data. Based on this information, the output slot has the ability to handle timing failures gracefully; the concrete output slot implementation is expected to provide some kind of de-click or de-flicker facility, which kicks in automatically when a timing failure is detected.
!!!usage and implementation
Clients retrieve just a reference to an output slot by asking a suitable OutputManager for an output possibility supporting a specific format. Usually, they just &quot;claim&quot; this slot by invoking {{{allocate()}}}, which behind the scenes causes building of the actual output connections and mechanisms. For each such connection -- corresponding to a single channel within the media format handled by this ~OutputSlot -- the client gets a smart-handle {{{DataSink}}}. The concrete ~OutputSlot implementation performs operations quite specific to the kind of output and external interface in question. All tese specific handling is embodied within the concrete connection implementation used by the concrete ~OutputSlot
@ -4259,7 +4259,7 @@ Placements have //value semantics,// i.e. we don't stress the identity of a plac
</pre>
</div>
<div title="PlacementIndex" modifier="Ichthyostega" modified="201001112323" created="200905090053" tags="SessionLogic spec impl" changecount="22">
<div title="PlacementIndex" modifier="Ichthyostega" modified="201112222246" created="200905090053" tags="SessionLogic spec impl" changecount="24">
<pre>An implementation facility used to keep track of individual Placements and their relations.
Especially, the [[Session]] maintains such an index, allowing to use the (opaque) PlacementRef tags for referring to a specific &quot;instance&quot; of an MObject, //placed// in a unique way into the current session. And, moreover, this index allows for one placement referring to another placement, so to implement a //relative// placement mode. Because there is an index behind the scenes, it is possible to actually access such a referral in the reverse direction, which is necessary for implementing the desired placement behaviour (if an object instance used as anchor is moved, all objects placed relatively to it have to move accordingly, which necessitates finding those other objects).
@ -4273,13 +4273,13 @@ Using a ''flat hashtable'' allows to access a Placement denoted by ID in O(1). T
* allowing to create a path &quot;up&quot; from this scope, which is used for resolving any queries
* (maybe/planned) relations to other placements
Alternatively, we could try to use a ''structure based index'', thereby avoiding the mentioned description record by folding any of the contained informations into the surrounding data structure:
Alternatively, we could try to use a ''structure based index'', thereby avoiding the mentioned description record by folding any of the contained information into the surrounding data structure:
* the scope would be obvious from the index, resp. from the path used to resolve this index
* any other informations, especially the relations, would be folded into the placement
* any other information, especially the relations, would be folded into the placement
* this way, the &quot;index&quot; could be reduced to being the session data structure itself.
//does a placement need to know it's own ID?//&amp;nbsp; Obviously, there needs to be a way to find out the ID for a given placement, especially in the following situations:
* for most of the operations above, when querying additional informations from index for a given placement
* for most of the operations above, when querying additional information from index for a given placement
* to create a PlacementRef (this is a variant of the &quot;shared ptr from this&quot;-problem)
On second sight, this problem turns out to be more involved, because either we have to keep a second index table for the reverse lookup (memory address -&gt; ID), or have to tie the placement by a back-link when adding it to the index/session data structure, or (alternatively) it forces us to store a copy of the ID //within// the placement itself. The last possibility seems to create the least impact; but implementing it this way effectively gears the implementation towards a hashtable based approach.
@ -4499,7 +4499,7 @@ This is the core service provided by the player subsystem. The purpose is to cre
:any details of this processing remain opaque for the clients; even the player subsystem just accesses the EngineFaçade
</pre>
</div>
<div title="Player" modifier="Ichthyostega" modified="201112091904" created="201012181700" tags="def overview" changecount="13">
<div title="Player" modifier="Ichthyostega" modified="201112222246" created="201012181700" tags="def overview" changecount="14">
<pre>Within Lumiera, &amp;raquo;Player&amp;laquo; is the name for a [[Subsystem]] responsible for organising and tracking //ongoing playback and render processes.// &amp;rarr; [[PlayProcess]]
The player subsystem does not perform or even manage any render operations, nor does it handle the outputs directly.
Yet it addresses some central concerns:
@ -4508,7 +4508,7 @@ Yet it addresses some central concerns:
:all playback and render processes are on equal footing, handled in a similar way.
;integration
:the player cares for the necessary integration with the other subsystems
:it consults the OutputManagement, retrieves the necessary informations from the [[Session]] and coordinates the forwarding of [[Backend]] calls.
:it consults the OutputManagement, retrieves the necessary information from the [[Session]] and coordinates the forwarding of [[Backend]] calls.
;time quantisation
:the player translates continuous time values into discrete frame counts.
:to perform this [[quantisation|TimeQuant]], the help of the session for building a TimeGrid for each output channel is required.
@ -4648,7 +4648,7 @@ The system is ''open'' inasmuch every part mirrors the structure of correspondin
&amp;rarr; see RenderProcess
</pre>
</div>
<div title="ProcPatt" modifier="Ichthyostega" modified="200805260329" created="200709212315" tags="def design" changecount="9">
<div title="ProcPatt" modifier="Ichthyostega" modified="201112222247" created="200709212315" tags="def design" changecount="10">
<pre>This special type of [[structural Asset|StructAsset]] represents information how to build some part of the render engine's processing nodes network. Processing patterns can be thought of as a blueprint or micro program for construction. Most notably, they are used for creating nodes reading, decoding and delivering source media material to the render network, and they are used for building the output connection via faders, summation or overlay nodes to the global pipes (busses). Each [[media Asset|MediaAsset]] has associated processing patterns describing the codecs and other transformations needed to get at the media data of this asset. (and because media assets are typically compound objects, the referred ~ProcPatt will be compound too). Similarily, for each stream kind, we can retrieve a processing pattern for making output connections. Obviously, the possibilities opened by using processing patterns go far beyond.
Technically, a processing pattern is a list of building instructions, which will be //executed// by the [[Builder]] on the render node network under construction. This implies the possibility to define further instruction kinds when needed in future; at the moment the relevant sorts of instructions are
@ -4662,7 +4662,7 @@ Like all [[structural assets|StructAsset]], ~ProcPatt employs a special naming s
The basic working set of processing patterns can be expected to be just there (hard wired or default configuration, mechanism for creating an sensible fallback). Besides, the idea is that new processing patterns can be added via rules in the session and then referred to by other rules controlling the build process. Any processing pattern is assembled by adding individual build instructions, or by including another (nested) processing pattern.
!!retrieving a suitable Processing Pattern
For a given situation, the necessary ProcPatt can be retrieved by issuing a [[configuration query|ConfigQuery]]. This query should include the needed capabilities in predicate form (technically this query is a Prolog goal), but it can leave out informations by just requesting &quot;the default&quot; &amp;rarr; see DefaultsManagement
For a given situation, the necessary ProcPatt can be retrieved by issuing a [[configuration query|ConfigQuery]]. This query should include the needed capabilities in predicate form (technically this query is a Prolog goal), but it can leave out some pieces of information by just requesting &quot;the default&quot; &amp;rarr; see DefaultsManagement
!!how does this actually work?
Any processing pattern needs the help of a passive holder tool suited for a specific [[building situation|BuilderPrimitives]]; we call these holder tools [[building moulds|BuilderMould]]. Depending on the situation, the mould has been armed up by the builder with the involved objects to be connected and extended. So, just by issuing the //location ID// defined within the individual build instruction (in most cases simply {{{&quot;current&quot;}}}), the processing pattern can retrieve the actual render object to use for building from the mould it is executed in.
@ -5078,10 +5078,10 @@ config.macros.rssFeedUpdate = {
//}}}
</pre>
</div>
<div title="RelationClipAsset" modifier="Ichthyostega" modified="200710212327" created="200710191541" tags="design decision img" changecount="7">
<div title="RelationClipAsset" modifier="Ichthyostega" modified="201112222247" created="200710191541" tags="design decision img" changecount="8">
<pre>What is the Role of the asset::Clip and how exactly are Assets and (Clip)-MObjects related?
First of all: ~MObjects are the dynamic/editing/manipulation view, while Assets are the static/bookkeeping/searching/information view of the same entities. Thus, the asset::Clip contains the general configuration, the ref to the media and descriptive properties, while all parameters being &quot;manipulated&quot; belong to the session::Clip (MObject). Besides that, the practical purpose of asset::Clip is that you can save and remember some selection as a Clip (Asset), maybe even attach some informations or markup to it, and later be able to (re)create a editable representation in the Session (the GUI could implement this by allowing to drag from the asset::Clip GUI representation to the timeline window)
First of all: ~MObjects are the dynamic/editing/manipulation view, while Assets are the static/bookkeeping/searching/information view of the same entities. Thus, the asset::Clip contains the general configuration, the ref to the media and descriptive properties, while all parameters being &quot;manipulated&quot; belong to the session::Clip (MObject). Besides that, the practical purpose of asset::Clip is that you can save and remember some selection as a Clip (Asset), maybe even attach some information or markup to it, and later be able to (re)create a editable representation in the Session (the GUI could implement this by allowing to drag from the asset::Clip GUI representation to the timeline window)
!!dependencies
The session::Clip (frequently called &quot;clip-MO&quot;, i.e. the MObject) //depends on the Asset.// It can't exist without the Asset, because the Asset is needed for rendering. The other direction is different: the asset::Clip knows that there is a dependant clip-MO, there could be //at most one// such clip-MO depending on the Asset, but the Asset can exist without the clip-MO (it gives the possibility to re-create the clip-MO).
@ -5361,8 +5361,8 @@ A sequence is always tied to a root-placed track, it can't exist without such. W
&amp;rarr; see detailed [[discussion of dependent objects' behaviour|ModelDependencies]]
</pre>
</div>
<div title="Session" modifier="Ichthyostega" modified="200911071800" created="200712100525" tags="def SessionLogic" changecount="8">
<pre>The Session contains all informations, state and objects to be edited by the User. From a users view, the Session is synonymous to the //current Project//. It can be [[saved and loaded|SessionLifecycle]]. The individual Objects within the Session, i.e. Clips, Media, Effects, are contained in one (or several) collections within the Session, which we call [[Sequence]].
<div title="Session" modifier="Ichthyostega" modified="201112222247" created="200712100525" tags="def SessionLogic" changecount="9">
<pre>The Session contains all information, state and objects to be edited by the User. From a users view, the Session is synonymous to the //current Project//. It can be [[saved and loaded|SessionLifecycle]]. The individual Objects within the Session, i.e. Clips, Media, Effects, are contained in one (or several) collections within the Session, which we call [[Sequence]].
&amp;rarr; [[Session design overview|SessionOverview]]
!Session structure
@ -5494,7 +5494,7 @@ Currently, I'm planning to modify MObjectRef to return only a const ref to the u
&lt;&lt;&lt;
</pre>
</div>
<div title="SessionLifecycle" modifier="Ichthyostega" modified="201010232310" created="200911070329" tags="SessionLogic spec" changecount="26">
<div title="SessionLifecycle" modifier="Ichthyostega" modified="201112222248" created="200911070329" tags="SessionLogic spec" changecount="27">
<pre>The current [[Session]] is the root of any state found within Proc-Layer. Thus, events defining the session's lifecycle influence and synchronise the cooperative behaviour of the entities within the model, the ProcDispatcher, [[Fixture]] and any facility below.
* when ''starting'', on first access an empty session is created, which puts any related facility into a defined initial state.
* when ''closing'' the session, any dependent facilities are disabled, disconnected, halted or closed
@ -5507,7 +5507,7 @@ The SessionManager is responsible for conducting the session lifecycle. Accessib
Beyond that, client code usually doesn't interact much with the lifecycle, which mostly is a pattern of events to happen in a well-defined sequence. So the //implementation// of the session management operations has to comply to this lifecycle, and does so by relying on a self-contained implementation service, the LifecycleAdvisor. But (contrary to an application framework) the lifecycle of the Lumiera session is rather fixed, the only possibility for configuration or extension being the [[lifecycle hooks|LifecycleEvent]], where other parts of the system (and even plug-ins) may install some callback methods.
!Synchronising access to session's implementation facilities
Some other parts and subsystems within the ~Proc-Layer need specialised access to implementation facilities within the session. Informations about some conditions and configurations might be retrieved through [[querrying the session|Query]], and especially default configurations for many objects are [[bound to the session|DefaultsImplementation]]. The [[discovery of session contents|SessionStructureQuery]] relies on an [[index facility|PlacementIndex]] embedded within the session implementation. Moreover, some &quot;properties&quot; of the [[media objects|MObject]] are actually due to the respective object being [[placed|Placement]] in some way into the session; consequently, there might be an dependency on the actual [[location as visible to the placement|PlacementScope]], which in turn is constituted by [[querying the index|QueryFocus]].
Some other parts and subsystems within the ~Proc-Layer need specialised access to implementation facilities within the session. Information about some conditions and configurations might be retrieved through [[querrying the session|Query]], and especially default configurations for many objects are [[bound to the session|DefaultsImplementation]]. The [[discovery of session contents|SessionStructureQuery]] relies on an [[index facility|PlacementIndex]] embedded within the session implementation. Moreover, some &quot;properties&quot; of the [[media objects|MObject]] are actually due to the respective object being [[placed|Placement]] in some way into the session; consequently, there might be an dependency on the actual [[location as visible to the placement|PlacementScope]], which in turn is constituted by [[querying the index|QueryFocus]].
Each of these facilities relies on a separate access point to session services, corresponding to distinct service interfaces. But &amp;mdash; on the implementation side &amp;mdash; all these services are provided by a (compound) SessionServices implementation object. This approach allows to switch the actual implementation of all these services simply by swapping the ~PImpl maintained by the session manager. A new implementation level service can thus be added to the ~SessionImpl just by hooking it into the ~SessionServices compound object. But note, this mechanism as such is ''not thread safe'', unless the //implementation// of the invoked functions is synchronised in some way to prevent switching to a new session implementation while another thread is still executing session implementation code.
@ -5539,8 +5539,8 @@ As detailed above, {{{Session::current}}} exposes the management / lifecycle API
{{red{none of the above is implemented as of 11/09}}}
</pre>
</div>
<div title="SessionLogic" modifier="Ichthyostega" modified="201003210012" created="200904242110" tags="overview" changecount="21">
<pre>The Session contains all informations, state and objects to be edited by the User (&amp;rarr;[[def|Session]]).
<div title="SessionLogic" modifier="Ichthyostega" modified="201112222248" created="200904242110" tags="overview" changecount="22">
<pre>The Session contains all information, state and objects to be edited by the User (&amp;rarr;[[def|Session]]).
As such, the SessionInterface is the main entrance point to Proc-Layer functionality, both for the primary EditingOperations and for playback/rendering processes. Proc-Layer state is rooted within the session and guided by the [[session's lifecycle events|SessionLifecycle]].
Implementation facilities within the Proc-Layer may access a somewhat richer [[session service API|SessionServices]].
@ -5575,14 +5575,14 @@ The answer is simple: the one who needs to know about their existence. Because b
Interestingly, there seems to be an alternative answer to this question. We could locate the setup and definition of all commands into a central administrative facility. Everyone in need of a command then ought to know the name and retrieve this command. Sounds like bureaucracy.
</pre>
</div>
<div title="SessionOverview" modifier="Ichthyostega" modified="200911071816" created="200709272105" tags="design img" changecount="38">
<div title="SessionOverview" modifier="Ichthyostega" modified="201112222248" created="200709272105" tags="design img" changecount="40">
<pre>&lt;&lt;&lt;
{{red{WARNING: Naming was discussed (11/08) and decided to be changed....}}}
* the term [[EDL]] was phased out in favour of ''Sequence''
* [[Session]] is largely synonymous to ''Project''
* there seems to be a new entity called [[Timeline]] which holds the global Pipes
&lt;&lt;&lt;
The [[Session]] (sometimes also called //Project// ) contains all informations and objects to be edited by the User. Any state within the Proc-Layer is directly or indirectly rooted in the session. It can be saved and loaded. The individual Objects within the Session, i.e. Clips, Media, Effects, are contained in one or multiple collections within the Session, which we call [[sequence(s)|Sequence]]. Moreover, the sesion contains references to all the Media files used, and it contains various default or user defined configuration, all being represented as [[Asset]]. At any given time, there is //only one current session// opened within the application. The [[lifecycle events|SessionLifecycle]] of the session define the lifecycle of ~Proc-Layer as a whole.
The [[Session]] (sometimes also called //Project// ) contains all information and objects to be edited by the User. Any state within the Proc-Layer is directly or indirectly rooted in the session. It can be saved and loaded. The individual Objects within the Session, i.e. Clips, Media, Effects, are contained in one or multiple collections within the Session, which we call [[sequence(s)|Sequence]]. Moreover, the sesion contains references to all the Media files used, and it contains various default or user defined configuration, all being represented as [[Asset]]. At any given time, there is //only one current session// opened within the application. The [[lifecycle events|SessionLifecycle]] of the session define the lifecycle of ~Proc-Layer as a whole.
The Session is close to what is visible in the GUI. From a user's perspective, you'll find a [[Timeline]]-like structure, containing an [[Sequence]], where various Media Objects are arranged and placed. The available building blocks and the rules how they can be combined together form Lumiera's [[high-level data model|HighLevelModel]]. Basically, besides the [[media objects|MObjects]] there are data connections and all processing is organized around processing chains or [[pipes|Pipe]], which can be either global (in the Session) or local (in real or virtual clips).
@ -7225,7 +7225,7 @@ Thus the possibly mutalble time entities get an {{{accept(time::Mutation&amp;)}}
Based on this time::Mutation design, we provide a specialised element for dealing with //running time values:// When attached to a target time entity, a &quot;life&quot; connection is established. From then on, continuous changes and mutations can be fed to the target by invoking a functor interface. Besides, a change notification signal (callback) can be installed, which will be invoked on each change. This {{{time::Control}}} element is the foundation for implementing all kinds of running time display widgets, spin buttons, timeline selections, playheads, loop playback and similar.
</pre>
</div>
<div title="TimeQuant" modifier="Ichthyostega" modified="201106080000" created="201012181753" tags="Concepts Player spec img discuss draft" changecount="52">
<div title="TimeQuant" modifier="Ichthyostega" modified="201112222249" created="201012181753" tags="Concepts Player spec img discuss draft" changecount="53">
<pre>The term &amp;raquo;Time&amp;laquo; spans a variety of vastly different entities. Within a NLE we get to deal with various //flavours of time values.//
;continuous time
:without any additional assumptions, ''points in time'' can be specified with arbitrary precision.
@ -7255,7 +7255,7 @@ Based on this time::Mutation design, we provide a specialised element for dealin
:This implies the presence of a //running synchronisation process,// with the authority to adjust the time base;
:contrast this to the internal time, which is static and unconnected --
;quantised time
:The ''act of quantisation'' transforms a continuous property into a ''discrete'' structure. Prominent examples can be found in the domain of micro physics and with digital information processing. In a broader sense, any measurement or //quantification// also encompasses a quantisation. Regarding time and time measurement, quantisation means alignment to a predefined ''time grid''. Quantisation necessarily is an //irreversible process// -- possible additional informations get discarded.
:The ''act of quantisation'' transforms a continuous property into a ''discrete'' structure. Prominent examples can be found in the domain of micro physics and with digital information processing. In a broader sense, any measurement or //quantification// also encompasses a quantisation. Regarding time and time measurement, quantisation means alignment to a predefined ''time grid''. Quantisation necessarily is an //irreversible process// -- possible additional information is discarded.
:Note that quantisation introduces an ''time origin'' and a ''reference scale''
;frame count
:within the context of film and media editing, the specification of a ''frame number'' is an especially important instance of quantisation.
@ -7526,7 +7526,7 @@ Using transitions is a very basic task and thus needs viable support by the GUI.
Because of this experience, ichthyo wants to support a more general case of transitions, which have N output connections, behave similar to their &quot;simple&quot; counterpart, but leave out the mixing step. As a plus, such transitions can be inserted at the source ports of N clips or between any intermediary or final output pipes as well. Any transition processor capable of handling this situation should provide some flag, in order to decide if he can be placed in such a manner. (wichin the builder, encountering a inconsistently placed transition is just an [[building error|BuildingError]])
</pre>
</div>
<div title="TypedID" modifier="Ichthyostega" modified="201012292351" created="201003200157" tags="Model Types Rules design draft" changecount="33">
<div title="TypedID" modifier="Ichthyostega" modified="201112222250" created="201003200157" tags="Model Types Rules design draft" changecount="34">
<pre>//drafted service as of 4/10 &amp;mdash; &amp;rarr;[[implementation plans|TypedLookup]]//
A registration service to associate object identities, symbolic identifiers and types.
@ -7552,7 +7552,7 @@ A registration service backed by an index table can be used to //translate//&amp
!!!{{red{WIP}}}Analysis and discussion
Still some contradictions &amp;mdash; and rather seems helpful, not so much necessary.
We //already have an registration service,// both for Assets (AssetManager) and for Placements (PlacementIndex). These facilities maintain not only a raw ID &amp;harr; object association, but also structuring informations, albeit bound to more specific circumstances (the system of placement scopes, and the asset category). The lookup uniqueID &amp;rArr; object could be implemented by sequentially querying this small number of central registration facilities. Thus, still lacking is a ''system of sub index tables''.
We //already have an registration service,// both for Assets (AssetManager) and for Placements (PlacementIndex). These facilities maintain not only a raw ID &amp;harr; object association, but also structuring information, albeit bound to more specific circumstances (the system of placement scopes, and the asset category). The lookup uniqueID &amp;rArr; object could be implemented by sequentially querying this small number of central registration facilities. Thus, still lacking is a ''system of sub index tables''.
As mentioned above, an ID &amp;harr; type association plays a crucial role when it comes to implementing any kind of rules based configuration. It would allow to bridge from our session objects to rules and resolution working entirely symbolic. (&amp;rarr; [[more|ConfigQueryIntegration]]). But, as of 3/2010 this is a //planned feature and not required to get the initial pipeline working.// Thus, according to the YAGNI principle, we shouldn't engage into any implementation details right now and just create the extension points.
@ -7573,7 +7573,7 @@ Just an ''registration scheme'' should be implemented right now, working complet
see [[implementation planning|TypedLookup]]
</pre>
</div>
<div title="TypedLookup" modifier="Ichthyostega" modified="201012292351" created="201004031607" tags="Rules Types spec impl draft" changecount="22">
<div title="TypedLookup" modifier="Ichthyostega" modified="201112222250" created="201004031607" tags="Rules Types spec impl draft" changecount="23">
<pre>TypedID is a registration service to associate object identities, symbolic identifiers and types. It acts as frontend to the TypedLookup service within Proc-Layer, at the implementation level. While TypedID works within a strictly typed context, this type information is translated into an internal index on passing over to the implementation, which manages a set of tables containing base entries with an combined symbolic+hash ID, plus an opaque buffer. Thus, the strictly typed context is required to re-access the stored data. But the type information wasn't erased entirely, so this typed context can be re-gained with the help of an internal type index. All of this is considered implementation detail and may be subject to change without further notice; any access is assumed to happen through the TypedID frontend. Besides, there are two more specialised frontends.
!Front-ends
@ -7584,7 +7584,7 @@ see [[implementation planning|TypedLookup]]
!Tables and index
The Table consists of several registration groups, each of which contains a hashtable and deals with one specific type. Groups are created on demand, but there is initially one group holding the internal type index (translation table). There may be even sub-groups, usable to create clusterings within one group.
__Individual entries__ are comprised of a EntryID as key (actually a ~BareEntryID, without the typing) and a payload, which //doesn't store data,// but informations necessary to ''lookup and access'' the registered object. Obviously, this information is type specific, and thus the ~TypedLookup implementation can't know how to deal with it directly. Rather, we store a ''functor'' in the payload of the type index group. This functor is directly linked to the TypeHandler, i.e. any type wanting to be a primary type within Lumiera, so as to be directly usable within the ConfigRules, needs to provide a suitable functor implementation through its ~TypeHandler. These functors are then invoked by the ~TypedID frontend, when it comes to re-accessing a registered entity by ID
__Individual entries__ are comprised of a EntryID as key (actually a ~BareEntryID, without the typing) and a payload, which //doesn't store data,// but information necessary to ''lookup and access'' the registered object. Obviously, this information is type specific, and thus the ~TypedLookup implementation can't know how to deal with it directly. Rather, we store a ''functor'' in the payload of the type index group. This functor is directly linked to the TypeHandler, i.e. any type wanting to be a primary type within Lumiera, so as to be directly usable within the ConfigRules, needs to provide a suitable functor implementation through its ~TypeHandler. These functors are then invoked by the ~TypedID frontend, when it comes to re-accessing a registered entity by ID
!link for automatic registration
An entity can be linked with the TypedLookup system to be registered and deregistered automatically. This is achieved by mixing in the {{{TypedID::Link}}}. On creation, this will set up an EntryID for this individual instance and cause creation of an empty entry within the suitable registration group. As a side-effect, uniqueness of any symbolic-ID within one group (type) is enforced. Obviously, the dtor of this registration Link cares for de-registration automatically. Be forwarned though, by creating an unique identity, this mechanism will interfere with copying and cloning of the registered entity.