mirror of https://github.com/KLayout/klayout.git
1185 lines
36 KiB
C++
1185 lines
36 KiB
C++
|
|
/*
|
|
|
|
KLayout Layout Viewer
|
|
Copyright (C) 2006-2017 Matthias Koefferlein
|
|
|
|
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*/
|
|
|
|
|
|
#ifndef _HDR_gsiSignals
|
|
#define _HDR_gsiSignals
|
|
|
|
#include "tlTypeTraits.h"
|
|
#include "tlObject.h"
|
|
#include "tlEvents.h"
|
|
#include "gsiTypes.h"
|
|
#include "gsiIterators.h"
|
|
#include "gsiCallback.h"
|
|
#include "gsiMethods.h"
|
|
#include "gsiSerialisation.h"
|
|
|
|
// On MinGW, the "access" macro will interfere with QMetaMethod's access method
|
|
#if defined(access)
|
|
# undef access
|
|
#endif
|
|
|
|
#include <QMetaMethod>
|
|
|
|
/**
|
|
* @brief A signal exposure framework
|
|
*
|
|
* Signals are a concept by which a piece of client code (script)
|
|
* can be bound to an event triggered by C++ code.
|
|
*
|
|
* This framework provides two basic interfaces: one for
|
|
* Qt signals and another one for tl::Event signals.
|
|
*
|
|
* 1.) Binding of tl::Event signals
|
|
*
|
|
* In C++:
|
|
* @code{.cpp}
|
|
* // tl::Event signals
|
|
* class X
|
|
* {
|
|
* public:
|
|
* void s_trigger(int n) { s(n); }
|
|
*
|
|
* public:
|
|
* // needs to be public
|
|
* tl::event<int> s;
|
|
* };
|
|
*
|
|
* gsi::ClassBase<X> cls_decl("X",
|
|
* gsi::event(&X::s, "s") +
|
|
* gsi::method(&x::s_trigger, "s_trigger")
|
|
* );
|
|
* @endcode
|
|
*
|
|
* In Ruby:
|
|
* @code{.rb}
|
|
* x = X::new()
|
|
* x.s { puts "x.s triggered" }
|
|
* x.s_trigger
|
|
* @endcode
|
|
*
|
|
* 2.) Binding of Qt signals:
|
|
*
|
|
* In C++:
|
|
* @code{.cpp}
|
|
* class Y
|
|
* : public QObject
|
|
* {
|
|
* Q_OBJECT
|
|
*
|
|
* signals:
|
|
* void s(int n);
|
|
*
|
|
* public:
|
|
* void s_trigger(int n) { emit s(n); }
|
|
* };
|
|
*
|
|
* gsi::ClassBase<Y> cls_decl("Y",
|
|
* gsi::qt_signal<int>("s(int)", "s") +
|
|
* gsi::method(&Y::s_trigger, "s_trigger")
|
|
* );
|
|
* @endcode
|
|
*
|
|
* In Ruby:
|
|
* @code{.rb}
|
|
* y = Y::new()
|
|
* y.s { puts "y.s triggered" }
|
|
* y.s_trigger
|
|
* @endcode
|
|
*/
|
|
|
|
namespace gsi
|
|
{
|
|
|
|
struct empty_list_t { };
|
|
template <class T, class H> struct type_pair_t { };
|
|
|
|
class SignalAdaptor;
|
|
|
|
/**
|
|
* @brief The signal handler provided by the client implementation
|
|
* The handler and the signal adaptor implement a double dispatch pattern:
|
|
* the handler implements the strategy how to call a signal on the client side.
|
|
* It is owned by the client. The adaptor implements the strategy how a signal
|
|
* is generated on the C++ side. The adaptor is owned by the handler.
|
|
*/
|
|
class GSI_PUBLIC SignalHandler
|
|
: public tl::Object
|
|
{
|
|
public:
|
|
/**
|
|
* @brief Constructor
|
|
*/
|
|
SignalHandler () { }
|
|
|
|
/**
|
|
* @brief Destructor
|
|
*/
|
|
virtual ~SignalHandler () { }
|
|
|
|
/**
|
|
* @brief Provides the implementation for the call of the signal
|
|
* The client-side implementation needs to reimplement this method to provide the mechanism
|
|
* how a signal is called. The implementation is supposed to take the arguments, call the
|
|
* function associated with this handler and place the return value in the ret serial buffer.
|
|
* @param method The method descriptor for the signal
|
|
* @param args The serialized arguments
|
|
* @param ret The return value
|
|
*/
|
|
virtual void call (const MethodBase *method, SerialArgs &args, SerialArgs &ret) const = 0;
|
|
|
|
/**
|
|
* @brief Ties the lifetime of a signal adaptor to that of the handler
|
|
* The system will call this method to install an adaptor with this handler.
|
|
* If the handler is deleted, the adaptor will be deleted too.
|
|
*/
|
|
void set_adaptor (SignalAdaptor *adaptor)
|
|
{
|
|
mp_adaptor.reset (adaptor);
|
|
}
|
|
|
|
private:
|
|
tl::shared_ptr<SignalAdaptor> mp_adaptor;
|
|
};
|
|
|
|
/**
|
|
* @brief A signal descriptor
|
|
* This is a specialization of the method descriptor for the signal.
|
|
*/
|
|
class GSI_PUBLIC Signal
|
|
: public MethodBase
|
|
{
|
|
public:
|
|
/**
|
|
* @brief Constructor
|
|
* @param name The name of the signal
|
|
* @param doc The documentation string
|
|
*/
|
|
Signal (const std::string &name, const std::string &doc)
|
|
: MethodBase (name, doc, false, false)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* @brief Returns a value indicating whether this method is a signal
|
|
*/
|
|
bool is_signal () const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief Registers a signal handler
|
|
* This method will register one new signal handler
|
|
* to the given object.
|
|
* The SignalHandler object is responsible for performing actions
|
|
* connected to the signal. The last handler's return value will
|
|
* be taken as the return value of the event if the event allows
|
|
* for a return value.
|
|
* The signal will *not* take ownership over the handler object.
|
|
*/
|
|
virtual void add_handler (void *obj, SignalHandler *handler) const = 0;
|
|
};
|
|
|
|
/**
|
|
* @brief A base class for signal adaptors
|
|
* Signal adaptors provide the C++ side of the signal double dispatch scheme.
|
|
* They implement the way a signal is generated.
|
|
*/
|
|
class GSI_PUBLIC SignalAdaptor
|
|
: public tl::Object
|
|
{
|
|
public:
|
|
/**
|
|
* @brief Constructor
|
|
*/
|
|
SignalAdaptor ()
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
/**
|
|
* @brief Destructor
|
|
*/
|
|
virtual ~SignalAdaptor ()
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
};
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// Adaptors for Qt signals
|
|
|
|
/**
|
|
* @brief A base class for Qt signals
|
|
* This object will act as a connector for Qt signals: when a signal is bound, this adaptor
|
|
* will provide a generic slot to connect the Qt signal to. The specializations will then
|
|
* forward this signal to the signal handler which itself is responsible for calling the
|
|
* client-side code.
|
|
*/
|
|
class GSI_PUBLIC QtSignalAdaptorBase
|
|
: public QObject, public SignalAdaptor
|
|
{
|
|
Q_OBJECT
|
|
|
|
public:
|
|
QtSignalAdaptorBase () { }
|
|
|
|
public slots:
|
|
void generic ()
|
|
{
|
|
tl_assert (false);
|
|
}
|
|
};
|
|
|
|
template <class T>
|
|
class QtSignalAdaptor;
|
|
|
|
/**
|
|
* @brief A specialization for signals without arguments
|
|
* In addition, this object provides the generic slot implementation which
|
|
* forwards the call to the client.
|
|
*/
|
|
template <>
|
|
class QtSignalAdaptor<empty_list_t>
|
|
: public QtSignalAdaptorBase
|
|
{
|
|
public:
|
|
/**
|
|
* @brief Constructor
|
|
* @param method The method descriptor
|
|
* @param handler The signal handler to call
|
|
*/
|
|
QtSignalAdaptor (const MethodBase *method, const SignalHandler *handler)
|
|
: mp_method (method), mp_handler (handler)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
virtual ~QtSignalAdaptor ()
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
protected:
|
|
/**
|
|
* @brief Provides a generic implementation of the slot
|
|
* This implementation basically hijacks the slot of QtSignalAdaptorBase. It won't call
|
|
* the real base class but QObject and handle every method not handled by QObject.
|
|
*/
|
|
virtual int qt_metacall (QMetaObject::Call c, int id, void **a)
|
|
{
|
|
id = QObject::qt_metacall (c, id, a);
|
|
if (id < 0) {
|
|
return id;
|
|
}
|
|
|
|
// we consume every invoked method here. This is some kind of dirty trick to
|
|
// override the moc-generated qt_metacall implementation by our custom one
|
|
// in the base class.
|
|
if (c == QMetaObject::InvokeMetaMethod && mp_handler) {
|
|
SerialArgs args (mp_method->argsize ());
|
|
write_args (args, a);
|
|
// .. further
|
|
SerialArgs ret (mp_method->retsize ());
|
|
mp_handler->call (mp_method, args, ret);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
protected:
|
|
virtual void write_args (SerialArgs &, void **)
|
|
{
|
|
// .. nothing for empty list ..
|
|
}
|
|
|
|
inline void write_args_non_virtual (SerialArgs &, void **)
|
|
{
|
|
// .. nothing for empty list ..
|
|
}
|
|
|
|
private:
|
|
const MethodBase *mp_method;
|
|
const SignalHandler *mp_handler;
|
|
};
|
|
|
|
/**
|
|
* @brief A specialization for signal with arguments
|
|
* This specialization is based on the typelist pattern and provides
|
|
* an implementation for the first argument - further arguments are
|
|
* provided by the base class.
|
|
*/
|
|
template <class H, class T>
|
|
class QtSignalAdaptor<type_pair_t<H, T> >
|
|
: public QtSignalAdaptor<T>
|
|
{
|
|
public:
|
|
QtSignalAdaptor (const MethodBase *method, SignalHandler *handler)
|
|
: QtSignalAdaptor<T> (method, handler)
|
|
{
|
|
// ...
|
|
}
|
|
|
|
protected:
|
|
/**
|
|
* @brief A helper class to write an argument of type X
|
|
* This helper class and it's specializations for references
|
|
* is required to avoid "taking pointer to reference" compiler
|
|
* errors.
|
|
*/
|
|
template <class X>
|
|
struct writer
|
|
{
|
|
void operator() (SerialArgs &args, void *a)
|
|
{
|
|
args.write<X> (*reinterpret_cast<X *> (a));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief A specialization of the argument writer for a const reference
|
|
*/
|
|
template <class X>
|
|
struct writer<const X &>
|
|
{
|
|
void operator() (SerialArgs &args, void *a)
|
|
{
|
|
args.write<const X &> (*reinterpret_cast<const X *> (a));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief A specialization of the argument writer for a non-const reference
|
|
*/
|
|
template <class X>
|
|
struct writer<X &>
|
|
{
|
|
void operator() (SerialArgs &args, void *a)
|
|
{
|
|
args.write<X &> (*reinterpret_cast<X *> (a));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief Serializes the arguments from the Qt argument stack to the argument buffer
|
|
* Takes one argument from the stack of arguments and writes it to the argument buffer.
|
|
* Then dispatches to the base class for the next arguments
|
|
*/
|
|
virtual void write_args (SerialArgs &args, void **a)
|
|
{
|
|
write_args_non_virtual (args, a);
|
|
}
|
|
|
|
/**
|
|
* @brief The actual implementation of the argument serialization
|
|
* To enable inlining of the argument serialization, this method is provided
|
|
* in a non-virtual way.
|
|
*/
|
|
inline void write_args_non_virtual (SerialArgs &args, void **a)
|
|
{
|
|
writer<H> () (args, *++a);
|
|
QtSignalAdaptor<T>::write_args_non_virtual (args, a);
|
|
}
|
|
};
|
|
|
|
template <class X>
|
|
class QtSignalImpl;
|
|
|
|
template <>
|
|
class QtSignalImpl<empty_list_t>
|
|
: public Signal
|
|
{
|
|
public:
|
|
QtSignalImpl (const char *signal, const std::string &name, const std::string &doc)
|
|
: Signal (name, doc), mp_signal (signal)
|
|
{
|
|
}
|
|
|
|
virtual MethodBase *clone () const
|
|
{
|
|
return new QtSignalImpl<empty_list_t> (*this);
|
|
}
|
|
|
|
void add_handler (void *obj, SignalHandler *handler) const
|
|
{
|
|
_add_handler<QtSignalAdaptor<empty_list_t> > (obj, handler);
|
|
}
|
|
|
|
void initialize ()
|
|
{
|
|
this->clear ();
|
|
_initialize ();
|
|
}
|
|
|
|
protected:
|
|
void _initialize ()
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
#if QT_VERSION >= 0x40800
|
|
template <class A>
|
|
void _add_handler (void *obj, SignalHandler *handler) const
|
|
{
|
|
// NOTE: this scheme requires 4.8 at least
|
|
|
|
QObject *qobj = (QObject *)obj;
|
|
|
|
A *adaptor = new A (this, handler);
|
|
// tie the lifetime of the adaptor to that of the handler
|
|
handler->set_adaptor (adaptor);
|
|
|
|
// NOTE: by connecting through QMetaMethod we force Qt into using the virtual qt_metacall
|
|
// rather than qt_static_metacall which we can't override ... Let's hope it stays like this ...
|
|
|
|
QByteArray sig = QMetaObject::normalizedSignature (mp_signal);
|
|
int sig_index = qobj->metaObject ()->indexOfMethod (sig.constData ());
|
|
if (sig_index < 0) {
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Not a valid signal: %1").arg (sig.constData ())));
|
|
}
|
|
|
|
QByteArray slot = QMetaObject::normalizedSignature ("generic()");
|
|
int slot_index = adaptor->metaObject ()->indexOfMethod (slot.constData ());
|
|
if (slot_index < 0) {
|
|
// NOTE: should not happen
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Not a valid slot: %1").arg (slot.constData ())));
|
|
}
|
|
|
|
QObject::connect (qobj, qobj->metaObject ()->method (sig_index), adaptor, adaptor->metaObject ()->method (slot_index));
|
|
}
|
|
#else
|
|
template <class A>
|
|
void _add_handler (void * /*obj*/, SignalHandler * /*handler*/) const
|
|
{
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Qt signal binding requires Qt version >= 4.8")));
|
|
}
|
|
#endif
|
|
|
|
private:
|
|
const char *mp_signal;
|
|
};
|
|
|
|
template <class H, class T>
|
|
class QtSignalImpl<type_pair_t<H, T> >
|
|
: public QtSignalImpl<T>
|
|
{
|
|
public:
|
|
QtSignalImpl (const char *signal, const std::string &name, const std::string &doc)
|
|
: QtSignalImpl<T> (signal, name, doc), m_s ()
|
|
{
|
|
}
|
|
|
|
template <class S>
|
|
QtSignalImpl<T> *def_arg (const gsi::ArgSpec<S> &s)
|
|
{
|
|
m_s = s;
|
|
return this;
|
|
}
|
|
|
|
virtual MethodBase *clone () const
|
|
{
|
|
return new QtSignalImpl<type_pair_t<H, T> > (*this);
|
|
}
|
|
|
|
void initialize ()
|
|
{
|
|
this->clear ();
|
|
_initialize ();
|
|
}
|
|
|
|
void add_handler (void *obj, SignalHandler *handler) const
|
|
{
|
|
QtSignalImpl<empty_list_t>::template _add_handler<QtSignalAdaptor<type_pair_t<H, T> > > (obj, handler);
|
|
}
|
|
|
|
protected:
|
|
void _initialize ()
|
|
{
|
|
this->template add_arg<H> (m_s);
|
|
QtSignalImpl<T>::_initialize ();
|
|
}
|
|
|
|
private:
|
|
gsi::ArgSpec<H> m_s;
|
|
};
|
|
|
|
/**
|
|
* @brief Provides the qt_signal wrapper for zero arguments
|
|
*/
|
|
inline Methods qt_signal (const char *signal, const std::string &name, const std::string &doc = std::string ())
|
|
{
|
|
return Methods(new QtSignalImpl<empty_list_t> (signal, name, doc));
|
|
}
|
|
|
|
/**
|
|
* @brief Provides the qt_signal wrapper for one argument
|
|
*/
|
|
template <class A1>
|
|
inline Methods qt_signal (const char *signal, const std::string &name, const std::string &doc = std::string ())
|
|
{
|
|
return Methods(new QtSignalImpl<type_pair_t<A1, empty_list_t> > (signal, name, doc));
|
|
}
|
|
|
|
/**
|
|
* @brief Provides the qt_signal wrapper for one argument
|
|
*/
|
|
template <class A1,
|
|
class S1>
|
|
inline Methods qt_signal (const char *signal, const std::string &name,
|
|
const ArgSpec<S1> &s1, const std::string &doc = std::string ())
|
|
{
|
|
return Methods((new QtSignalImpl<type_pair_t<A1, empty_list_t> > (signal, name, doc))->def_arg (s1));
|
|
}
|
|
|
|
// ...
|
|
template <class A1, class A2>
|
|
inline Methods qt_signal (const char *signal, const std::string &name, const std::string &doc = std::string ())
|
|
{
|
|
return Methods(new QtSignalImpl<type_pair_t<A1, type_pair_t<A2, empty_list_t> > > (signal, name, doc));
|
|
}
|
|
|
|
// ...
|
|
template <class A1, class A2,
|
|
class S1, class S2>
|
|
inline Methods qt_signal (const char *signal, const std::string &name,
|
|
const ArgSpec<S1> &s1, const ArgSpec<S2> &s2, const std::string &doc = std::string ())
|
|
{
|
|
return Methods((new QtSignalImpl<type_pair_t<A1, type_pair_t<A2, empty_list_t> > > (signal, name, doc))
|
|
->def_arg (s1)->def_arg (s2));
|
|
}
|
|
|
|
// ...
|
|
template <class A1, class A2, class A3>
|
|
inline Methods qt_signal (const char *signal, const std::string &name, const std::string &doc = std::string ())
|
|
{
|
|
return Methods(new QtSignalImpl<type_pair_t<A1, type_pair_t<A2, type_pair_t<A3, empty_list_t> > > > (signal, name, doc));
|
|
}
|
|
|
|
// ...
|
|
template <class A1, class A2, class A3,
|
|
class S1, class S2, class S3>
|
|
inline Methods qt_signal (const char *signal, const std::string &name,
|
|
const ArgSpec<S1> &s1, const ArgSpec<S2> &s2, const ArgSpec<S3> &s3, const std::string &doc = std::string ())
|
|
{
|
|
return Methods((new QtSignalImpl<type_pair_t<A1, type_pair_t<A2, type_pair_t<A3, empty_list_t> > > > (signal, name, doc))
|
|
->def_arg (s1)->def_arg (s2)->def_arg (s3));
|
|
}
|
|
|
|
// ...
|
|
template <class A1, class A2, class A3, class A4>
|
|
inline Methods qt_signal (const char *signal, const std::string &name, const std::string &doc = std::string ())
|
|
{
|
|
return Methods(new QtSignalImpl<type_pair_t<A1, type_pair_t<A2, type_pair_t<A3, type_pair_t<A4, empty_list_t> > > > > (signal, name, doc));
|
|
}
|
|
|
|
// ...
|
|
template <class A1, class A2, class A3, class A4,
|
|
class S1, class S2, class S3, class S4>
|
|
inline Methods qt_signal (const char *signal, const std::string &name,
|
|
const ArgSpec<S1> &s1, const ArgSpec<S2> &s2, const ArgSpec<S3> &s3, const ArgSpec<S4> &s4, const std::string &doc = std::string ())
|
|
{
|
|
return Methods((new QtSignalImpl<type_pair_t<A1, type_pair_t<A2, type_pair_t<A3, type_pair_t<A4, empty_list_t> > > > > (signal, name, doc))
|
|
->def_arg (s1)->def_arg (s2)->def_arg (s3)->def_arg (s4));
|
|
}
|
|
|
|
// ...
|
|
template <class A1, class A2, class A3, class A4, class A5>
|
|
inline Methods qt_signal (const char *signal, const std::string &name, const std::string &doc = std::string ())
|
|
{
|
|
return Methods(new QtSignalImpl<type_pair_t<A1, type_pair_t<A2, type_pair_t<A3, type_pair_t<A4, type_pair_t<A5, empty_list_t> > > > > > (signal, name, doc));
|
|
}
|
|
|
|
// ...
|
|
template <class A1, class A2, class A3, class A4, class A5,
|
|
class S1, class S2, class S3, class S4, class S5>
|
|
inline Methods qt_signal (const char *signal, const std::string &name,
|
|
const ArgSpec<S1> &s1, const ArgSpec<S2> &s2, const ArgSpec<S3> &s3, const ArgSpec<S4> &s4, const ArgSpec<S5> &s5, const std::string &doc = std::string ())
|
|
{
|
|
return Methods((new QtSignalImpl<type_pair_t<A1, type_pair_t<A2, type_pair_t<A3, type_pair_t<A4, type_pair_t<A5, empty_list_t> > > > > > (signal, name, doc))
|
|
->def_arg (s1)->def_arg (s2)->def_arg (s3)->def_arg (s4)->def_arg (s5));
|
|
}
|
|
|
|
// ...
|
|
template <class A1, class A2, class A3, class A4, class A5, class A6>
|
|
inline Methods qt_signal (const char *signal, const std::string &name, const std::string &doc = std::string ())
|
|
{
|
|
return Methods(new QtSignalImpl<type_pair_t<A1, type_pair_t<A2, type_pair_t<A3, type_pair_t<A4, type_pair_t<A5, type_pair_t<A6, empty_list_t> > > > > > > (signal, name, doc));
|
|
}
|
|
|
|
// ...
|
|
template <class A1, class A2, class A3, class A4, class A5, class A6,
|
|
class S1, class S2, class S3, class S4, class S5, class S6>
|
|
inline Methods qt_signal (const char *signal, const std::string &name,
|
|
const ArgSpec<S1> &s1, const ArgSpec<S2> &s2, const ArgSpec<S3> &s3, const ArgSpec<S4> &s4, const ArgSpec<S5> &s5, const ArgSpec<S6> &s6, const std::string &doc = std::string ())
|
|
{
|
|
return Methods((new QtSignalImpl<type_pair_t<A1, type_pair_t<A2, type_pair_t<A3, type_pair_t<A4, type_pair_t<A5, type_pair_t<A6, empty_list_t> > > > > > > (signal, name, doc))
|
|
->def_arg (s1)->def_arg (s2)->def_arg (s3)->def_arg (s4)->def_arg (s5)->def_arg (s6));
|
|
}
|
|
|
|
// ...
|
|
template <class A1, class A2, class A3, class A4, class A5, class A6, class A7>
|
|
inline Methods qt_signal (const char *signal, const std::string &name, const std::string &doc = std::string ())
|
|
{
|
|
return Methods(new QtSignalImpl<type_pair_t<A1, type_pair_t<A2, type_pair_t<A3, type_pair_t<A4, type_pair_t<A5, type_pair_t<A6, type_pair_t<A7, empty_list_t> > > > > > > > (signal, name, doc));
|
|
}
|
|
|
|
// ...
|
|
template <class A1, class A2, class A3, class A4, class A5, class A6, class A7,
|
|
class S1, class S2, class S3, class S4, class S5, class S6, class S7>
|
|
inline Methods qt_signal (const char *signal, const std::string &name,
|
|
const ArgSpec<S1> &s1, const ArgSpec<S2> &s2, const ArgSpec<S3> &s3, const ArgSpec<S4> &s4, const ArgSpec<S5> &s5, const ArgSpec<S6> &s6, const ArgSpec<S7> &s7, const std::string &doc = std::string ())
|
|
{
|
|
return Methods((new QtSignalImpl<type_pair_t<A1, type_pair_t<A2, type_pair_t<A3, type_pair_t<A4, type_pair_t<A5, type_pair_t<A6, type_pair_t<A7, empty_list_t> > > > > > > > (signal, name, doc))
|
|
->def_arg (s1)->def_arg (s2)->def_arg (s3)->def_arg (s4)->def_arg (s5)->def_arg (s6)->def_arg (s7));
|
|
}
|
|
|
|
// ...
|
|
template <class A1, class A2, class A3, class A4, class A5, class A6, class A7, class A8>
|
|
inline Methods qt_signal (const char *signal, const std::string &name, const std::string &doc = std::string ())
|
|
{
|
|
return Methods(new QtSignalImpl<type_pair_t<A1, type_pair_t<A2, type_pair_t<A3, type_pair_t<A4, type_pair_t<A5, type_pair_t<A6, type_pair_t<A7, type_pair_t<A8, empty_list_t> > > > > > > > > (signal, name, doc));
|
|
}
|
|
|
|
// ...
|
|
template <class A1, class A2, class A3, class A4, class A5, class A6, class A7, class A8,
|
|
class S1, class S2, class S3, class S4, class S5, class S6, class S7, class S8>
|
|
inline Methods qt_signal (const char *signal, const std::string &name,
|
|
const ArgSpec<S1> &s1, const ArgSpec<S2> &s2, const ArgSpec<S3> &s3, const ArgSpec<S4> &s4, const ArgSpec<S5> &s5, const ArgSpec<S6> &s6, const ArgSpec<S7> &s7, const ArgSpec<S8> &s8, const std::string &doc = std::string ())
|
|
{
|
|
return Methods((new QtSignalImpl<type_pair_t<A1, type_pair_t<A2, type_pair_t<A3, type_pair_t<A4, type_pair_t<A5, type_pair_t<A6, type_pair_t<A7, type_pair_t<A8, empty_list_t> > > > > > > > > (signal, name, doc))
|
|
->def_arg (s1)->def_arg (s2)->def_arg (s3)->def_arg (s4)->def_arg (s5)->def_arg (s6)->def_arg (s7)->def_arg (s8));
|
|
}
|
|
|
|
// ...
|
|
template <class A1, class A2, class A3, class A4, class A5, class A6, class A7, class A8, class A9>
|
|
inline Methods qt_signal (const char *signal, const std::string &name, const std::string &doc)
|
|
{
|
|
return Methods(new QtSignalImpl<type_pair_t<A1, type_pair_t<A2, type_pair_t<A3, type_pair_t<A4, type_pair_t<A5, type_pair_t<A6, type_pair_t<A7, type_pair_t<A8, type_pair_t<A9, empty_list_t> > > > > > > > > > (signal, name, doc));
|
|
}
|
|
|
|
// ...
|
|
template <class A1, class A2, class A3, class A4, class A5, class A6, class A7, class A8, class A9,
|
|
class S1, class S2, class S3, class S4, class S5, class S6, class S7, class S8, class S9>
|
|
inline Methods qt_signal (const char *signal, const std::string &name,
|
|
const ArgSpec<S1> &s1, const ArgSpec<S2> &s2, const ArgSpec<S3> &s3, const ArgSpec<S4> &s4, const ArgSpec<S5> &s5, const ArgSpec<S6> &s6, const ArgSpec<S7> &s7, const ArgSpec<S8> &s8, const ArgSpec<S9> &s9, const std::string &doc = std::string ())
|
|
{
|
|
return Methods((new QtSignalImpl<type_pair_t<A1, type_pair_t<A2, type_pair_t<A3, type_pair_t<A4, type_pair_t<A5, type_pair_t<A6, type_pair_t<A7, type_pair_t<A8, type_pair_t<A9, empty_list_t> > > > > > > > > > (signal, name, doc))
|
|
->def_arg (s1)->def_arg (s2)->def_arg (s3)->def_arg (s4)->def_arg (s5)->def_arg (s6)->def_arg (s7)->def_arg (s8)->def_arg (s9));
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// Adaptors for tl::event
|
|
|
|
template <class T>
|
|
class EventSignalAdaptor;
|
|
|
|
/**
|
|
* @brief A signal adaptor for tl::event signals without arguments
|
|
*/
|
|
template <>
|
|
class EventSignalAdaptor<empty_list_t>
|
|
: public SignalAdaptor
|
|
{
|
|
public:
|
|
/**
|
|
* @brief Constructor
|
|
* @param method The method descriptor
|
|
* @param handler The signal handler to call
|
|
*/
|
|
EventSignalAdaptor (const MethodBase *method, const SignalHandler *handler)
|
|
: mp_method (method), mp_handler (handler)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
/**
|
|
* @brief The actual target for the signal
|
|
*/
|
|
void event_receiver (int /*argc*/, void ** /*args*/)
|
|
{
|
|
if (mp_handler) {
|
|
SerialArgs args (mp_method->argsize ());
|
|
SerialArgs ret (mp_method->retsize ());
|
|
mp_handler->call (mp_method, args, ret);
|
|
}
|
|
}
|
|
|
|
protected:
|
|
const SignalHandler *handler () const
|
|
{
|
|
return mp_handler;
|
|
}
|
|
|
|
const MethodBase *method () const
|
|
{
|
|
return mp_method;
|
|
}
|
|
|
|
void write_args (SerialArgs &, void **)
|
|
{
|
|
// .. no arguments to write ..
|
|
}
|
|
|
|
private:
|
|
const MethodBase *mp_method;
|
|
const SignalHandler *mp_handler;
|
|
};
|
|
|
|
/**
|
|
* @brief A specialization for signal with arguments
|
|
* This specialization is based on the typelist pattern and provides
|
|
* an implementation for the first argument - further arguments are
|
|
* provided by the base class.
|
|
*/
|
|
template <class H, class T>
|
|
class EventSignalAdaptor<type_pair_t<H, T> >
|
|
: public EventSignalAdaptor<T>
|
|
{
|
|
public:
|
|
EventSignalAdaptor (const MethodBase *method, SignalHandler *handler)
|
|
: EventSignalAdaptor<T> (method, handler)
|
|
{
|
|
// ...
|
|
}
|
|
|
|
/**
|
|
* @brief The actual target for the signal
|
|
*/
|
|
void event_receiver (int /*argc*/, void **a)
|
|
{
|
|
if (this->handler ()) {
|
|
SerialArgs args (this->method ()->argsize ());
|
|
write_args (args, a);
|
|
// .. further
|
|
SerialArgs ret (this->method ()->retsize ());
|
|
this->handler ()->call (this->method (), args, ret);
|
|
}
|
|
}
|
|
|
|
protected:
|
|
/**
|
|
* @brief A helper class to write an argument of type X
|
|
* This helper class and it's specializations for references
|
|
* is required to avoid "taking pointer to reference" compiler
|
|
* errors.
|
|
*/
|
|
template <class X>
|
|
struct writer
|
|
{
|
|
void operator() (SerialArgs &args, void *a)
|
|
{
|
|
args.write<X> (*reinterpret_cast<X *> (a));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief A specialization of the argument writer for a const reference
|
|
*/
|
|
template <class X>
|
|
struct writer<const X &>
|
|
{
|
|
void operator() (SerialArgs &args, void *a)
|
|
{
|
|
args.write<const X &> (*reinterpret_cast<const X *> (a));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief A specialization of the argument writer for a non-const reference
|
|
*/
|
|
template <class X>
|
|
struct writer<X &>
|
|
{
|
|
void operator() (SerialArgs &args, void *a)
|
|
{
|
|
args.write<X &> (*reinterpret_cast<X *> (a));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief Serializes the arguments from the Qt argument stack to the argument buffer
|
|
* Takes one argument from the stack of arguments and writes it to the argument buffer.
|
|
* Then dispatches to the base class for the next arguments
|
|
*/
|
|
void write_args (SerialArgs &args, void **a)
|
|
{
|
|
writer<H> () (args, *a++);
|
|
EventSignalAdaptor<T>::write_args (args, a);
|
|
}
|
|
};
|
|
|
|
template <class X, class E, class TL>
|
|
class EventSignalImpl;
|
|
|
|
template <class X, class E>
|
|
class EventSignalImpl<X, E, empty_list_t>
|
|
: public Signal
|
|
{
|
|
public:
|
|
typedef E event_type;
|
|
|
|
EventSignalImpl (event_type (X::*event), const std::string &name, const std::string &doc)
|
|
: Signal (name, doc), mp_event (event)
|
|
{
|
|
}
|
|
|
|
virtual MethodBase *clone () const
|
|
{
|
|
return new EventSignalImpl<X, E, empty_list_t> (*this);
|
|
}
|
|
|
|
void add_handler (void *obj, SignalHandler *handler) const
|
|
{
|
|
_add_handler<EventSignalAdaptor<empty_list_t> > (obj, handler);
|
|
}
|
|
|
|
void initialize ()
|
|
{
|
|
this->clear ();
|
|
_initialize ();
|
|
}
|
|
|
|
protected:
|
|
template <class A>
|
|
void _add_handler (void *obj, SignalHandler *handler) const
|
|
{
|
|
A *adaptor = new A (this, handler);
|
|
// tie the lifetime of the adaptor to that of the handler
|
|
handler->set_adaptor (adaptor);
|
|
|
|
X *x = (X *)obj;
|
|
(x->*mp_event).add (adaptor, &A::event_receiver);
|
|
}
|
|
|
|
void _initialize ()
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
private:
|
|
event_type (X::*mp_event);
|
|
};
|
|
|
|
template <class X, class E, class H, class T>
|
|
class EventSignalImpl<X, E, type_pair_t<H, T> >
|
|
: public EventSignalImpl<X, E, T>
|
|
{
|
|
public:
|
|
typedef E event_type;
|
|
|
|
EventSignalImpl (event_type (X::*event), const std::string &name, const std::string &doc)
|
|
: EventSignalImpl<X, E, T> (event, name, doc)
|
|
{
|
|
}
|
|
|
|
virtual MethodBase *clone () const
|
|
{
|
|
return new EventSignalImpl<X, E, type_pair_t<H, T> > (*this);
|
|
}
|
|
|
|
void add_handler (void *obj, SignalHandler *handler) const
|
|
{
|
|
EventSignalImpl<X, E, empty_list_t>::template _add_handler<EventSignalAdaptor<type_pair_t<H, T> > > (obj, handler);
|
|
}
|
|
|
|
template <class S>
|
|
EventSignalImpl<X, E, T> *def_arg (const gsi::ArgSpec<S> &s)
|
|
{
|
|
m_s = s;
|
|
return this;
|
|
}
|
|
|
|
void initialize ()
|
|
{
|
|
this->clear ();
|
|
_initialize ();
|
|
}
|
|
|
|
protected:
|
|
void _initialize ()
|
|
{
|
|
this->template add_arg<H> (m_s);
|
|
EventSignalImpl<X, E, T>::_initialize ();
|
|
}
|
|
|
|
private:
|
|
gsi::ArgSpec<H> m_s;
|
|
};
|
|
|
|
template <class X, class E, class TL>
|
|
class EventSignalFuncImpl;
|
|
|
|
template <class X, class E>
|
|
class EventSignalFuncImpl<X, E, empty_list_t>
|
|
: public Signal
|
|
{
|
|
public:
|
|
typedef E event_type;
|
|
|
|
EventSignalFuncImpl (event_type &(*event) (X *), const std::string &name, const std::string &doc)
|
|
: Signal (name, doc), mp_event (event)
|
|
{
|
|
}
|
|
|
|
virtual MethodBase *clone () const
|
|
{
|
|
return new EventSignalFuncImpl<X, E, empty_list_t> (*this);
|
|
}
|
|
|
|
void add_handler (void *obj, SignalHandler *handler) const
|
|
{
|
|
_add_handler<EventSignalAdaptor<empty_list_t> > (obj, handler);
|
|
}
|
|
|
|
void initialize ()
|
|
{
|
|
this->clear ();
|
|
_initialize ();
|
|
}
|
|
|
|
protected:
|
|
template <class A>
|
|
void _add_handler (void *obj, SignalHandler *handler) const
|
|
{
|
|
A *adaptor = new A (this, handler);
|
|
// tie the lifetime of the adaptor to that of the handler
|
|
handler->set_adaptor (adaptor);
|
|
|
|
X *x = (X *)obj;
|
|
(*mp_event) (x).add (adaptor, &A::event_receiver);
|
|
}
|
|
|
|
void _initialize ()
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
private:
|
|
event_type &(*mp_event) (X *x);
|
|
};
|
|
|
|
template <class X, class E, class H, class T>
|
|
class EventSignalFuncImpl<X, E, type_pair_t<H, T> >
|
|
: public EventSignalFuncImpl<X, E, T>
|
|
{
|
|
public:
|
|
typedef E event_type;
|
|
|
|
EventSignalFuncImpl (event_type &(*event) (X *), const std::string &name, const std::string &doc)
|
|
: EventSignalFuncImpl<X, E, T> (event, name, doc)
|
|
{
|
|
}
|
|
|
|
virtual MethodBase *clone () const
|
|
{
|
|
return new EventSignalFuncImpl<X, E, type_pair_t<H, T> > (*this);
|
|
}
|
|
|
|
void add_handler (void *obj, SignalHandler *handler) const
|
|
{
|
|
EventSignalFuncImpl<X, E, empty_list_t>::template _add_handler<EventSignalAdaptor<type_pair_t<H, T> > > (obj, handler);
|
|
}
|
|
|
|
template <class S>
|
|
EventSignalFuncImpl<X, E, T> *def_arg (const gsi::ArgSpec<S> &s)
|
|
{
|
|
m_s = s;
|
|
return this;
|
|
}
|
|
|
|
void initialize ()
|
|
{
|
|
this->clear ();
|
|
_initialize ();
|
|
}
|
|
|
|
protected:
|
|
void _initialize ()
|
|
{
|
|
this->template add_arg<H> (m_s);
|
|
EventSignalFuncImpl<X, E, T>::_initialize ();
|
|
}
|
|
|
|
private:
|
|
gsi::ArgSpec<H> m_s;
|
|
};
|
|
|
|
/**
|
|
* @brief Provides the tl::event wrapper for zero arguments
|
|
*/
|
|
template <class X>
|
|
Methods event (const std::string &name, tl::event<> (X::*event), const std::string &doc = std::string ())
|
|
{
|
|
return Methods(new EventSignalImpl<X, tl::event<>, empty_list_t> (event, name, doc));
|
|
}
|
|
|
|
/**
|
|
* @brief Provides the tl::event wrapper for one argument
|
|
*/
|
|
template <class X, class A1>
|
|
inline Methods event (const std::string &name, tl::event<A1> (X::*event), const std::string &doc = std::string ())
|
|
{
|
|
return Methods(new EventSignalImpl<X, tl::event<A1>, type_pair_t<A1, empty_list_t> > (event, name, doc));
|
|
}
|
|
|
|
/**
|
|
* @brief Provides the tl::event wrapper for one argument
|
|
*/
|
|
template <class X, class A1, class S1>
|
|
inline Methods event (const std::string &name, tl::event<A1> (X::*event),
|
|
const ArgSpec<S1> &s1, const std::string &doc = std::string ())
|
|
{
|
|
return Methods((new EventSignalImpl<X, tl::event<A1>, type_pair_t<A1, empty_list_t> > (event, name, doc))
|
|
->def_arg (s1));
|
|
}
|
|
|
|
// ...
|
|
template <class X, class A1, class A2>
|
|
inline Methods event (const std::string &name, tl::event<A1, A2> (X::*event), const std::string &doc = std::string ())
|
|
{
|
|
return Methods(new EventSignalImpl<X, tl::event<A1, A2>, type_pair_t<A1, type_pair_t<A2, empty_list_t> > > (event, name, doc));
|
|
}
|
|
|
|
// ...
|
|
template <class X, class A1, class A2, class S1, class S2>
|
|
inline Methods event (const std::string &name, tl::event<A1, A2> (X::*event),
|
|
const ArgSpec<S1> &s1, const ArgSpec<S2> &s2, const std::string &doc = std::string ())
|
|
{
|
|
return Methods((new EventSignalImpl<X, tl::event<A1, A2>, type_pair_t<A1, type_pair_t<A2, empty_list_t> > > (event, name, doc))
|
|
->def_arg (s1)->def_arg (s2));
|
|
}
|
|
|
|
// ...
|
|
template <class X, class A1, class A2, class A3>
|
|
inline Methods event (const std::string &name, tl::event<A1, A2, A3> (X::*event), const std::string &doc = std::string ())
|
|
{
|
|
return Methods(new EventSignalImpl<X, tl::event<A1, A2, A3>, type_pair_t<A1, type_pair_t<A2, type_pair_t<A3, empty_list_t> > > > (event, name, doc));
|
|
}
|
|
|
|
// ...
|
|
template <class X, class A1, class A2, class A3, class S1, class S2, class S3>
|
|
inline Methods event (const std::string &name, tl::event<A1, A2, A3> (X::*event),
|
|
const ArgSpec<S1> &s1, const ArgSpec<S2> &s2, const ArgSpec<S3> &s3, const std::string &doc = std::string ())
|
|
{
|
|
return Methods((new EventSignalImpl<X, tl::event<A1, A2, A3>, type_pair_t<A1, type_pair_t<A2, type_pair_t<A3, empty_list_t> > > > (event, name, doc))
|
|
->def_arg (s1)->def_arg (s2)->def_arg (s3));
|
|
}
|
|
|
|
// ...
|
|
template <class X, class A1, class A2, class A3, class A4>
|
|
inline Methods event (const std::string &name, tl::event<A1, A2, A3, A4> (X::*event), const std::string &doc = std::string ())
|
|
{
|
|
return Methods(new EventSignalImpl<X, tl::event<A1, A2, A3, A4>, type_pair_t<A1, type_pair_t<A2, type_pair_t<A3, type_pair_t<A4, empty_list_t> > > > > (event, name, doc));
|
|
}
|
|
|
|
// ...
|
|
template <class X, class A1, class A2, class A3, class A4, class S1, class S2, class S3, class S4>
|
|
inline Methods event (const std::string &name, tl::event<A1, A2, A3, A4> (X::*event),
|
|
const ArgSpec<S1> &s1, const ArgSpec<S2> &s2, const ArgSpec<S3> &s3, const ArgSpec<S4> &s4, const std::string &doc = std::string ())
|
|
{
|
|
return Methods((new EventSignalImpl<X, tl::event<A1, A2, A3, A4>, type_pair_t<A1, type_pair_t<A2, type_pair_t<A3, type_pair_t<A4, empty_list_t> > > > > (event, name, doc))
|
|
->def_arg (s1)->def_arg (s2)->def_arg (s3)->def_arg (s4));
|
|
}
|
|
|
|
/**
|
|
* @brief Provides the externalized tl::event wrapper for zero arguments
|
|
*/
|
|
template <class X>
|
|
Methods event_ext (const std::string &name, tl::event<> &(*event) (X *), const std::string &doc = std::string ())
|
|
{
|
|
return Methods(new EventSignalFuncImpl<X, tl::event<>, empty_list_t> (event, name, doc));
|
|
}
|
|
|
|
/**
|
|
* @brief Provides the externalized tl::event wrapper for one argument
|
|
*/
|
|
template <class X, class A1>
|
|
inline Methods event_ext (const std::string &name, tl::event<A1> &(*event) (X *), const std::string &doc = std::string ())
|
|
{
|
|
return Methods(new EventSignalFuncImpl<X, tl::event<A1>, type_pair_t<A1, empty_list_t> > (event, name, doc));
|
|
}
|
|
|
|
/**
|
|
* @brief Provides the externalized tl::event wrapper for one argument
|
|
*/
|
|
template <class X, class A1, class S1>
|
|
inline Methods event_ext (const std::string &name, tl::event<A1> &(*event) (X *),
|
|
const ArgSpec<S1> &s1, const std::string &doc = std::string ())
|
|
{
|
|
return Methods((new EventSignalFuncImpl<X, tl::event<A1>, type_pair_t<A1, empty_list_t> > (event, name, doc))
|
|
->def_arg (s1));
|
|
}
|
|
|
|
// ...
|
|
template <class X, class A1, class A2>
|
|
inline Methods event_ext (const std::string &name, tl::event<A1, A2> &(*event) (X *), const std::string &doc = std::string ())
|
|
{
|
|
return Methods(new EventSignalFuncImpl<X, tl::event<A1, A2>, type_pair_t<A1, type_pair_t<A2, empty_list_t> > > (event, name, doc));
|
|
}
|
|
|
|
// ...
|
|
template <class X, class A1, class A2, class S1, class S2>
|
|
inline Methods event_ext (const std::string &name, tl::event<A1, A2> &(*event) (X *),
|
|
const ArgSpec<S1> &s1, const ArgSpec<S2> &s2, const std::string &doc = std::string ())
|
|
{
|
|
return Methods((new EventSignalFuncImpl<X, tl::event<A1, A2>, type_pair_t<A1, type_pair_t<A2, empty_list_t> > > (event, name, doc))
|
|
->def_arg (s1)->def_arg (s2));
|
|
}
|
|
|
|
// ...
|
|
template <class X, class A1, class A2, class A3>
|
|
inline Methods event_ext (const std::string &name, tl::event<A1, A2, A3> &(*event) (X *), const std::string &doc = std::string ())
|
|
{
|
|
return Methods(new EventSignalFuncImpl<X, tl::event<A1, A2, A3>, type_pair_t<A1, type_pair_t<A2, type_pair_t<A3, empty_list_t> > > > (event, name, doc));
|
|
}
|
|
|
|
// ...
|
|
template <class X, class A1, class A2, class A3, class S1, class S2, class S3>
|
|
inline Methods event_ext (const std::string &name, tl::event<A1, A2, A3> &(*event) (X *),
|
|
const ArgSpec<S1> &s1, const ArgSpec<S2> &s2, const ArgSpec<S3> &s3, const std::string &doc = std::string ())
|
|
{
|
|
return Methods((new EventSignalFuncImpl<X, tl::event<A1, A2, A3>, type_pair_t<A1, type_pair_t<A2, type_pair_t<A3, empty_list_t> > > > (event, name, doc))
|
|
->def_arg (s1)->def_arg (s2)->def_arg (s3));
|
|
}
|
|
|
|
// ...
|
|
template <class X, class A1, class A2, class A3, class A4>
|
|
inline Methods event_ext (const std::string &name, tl::event<A1, A2, A3, A4> &(*event) (X *), const std::string &doc = std::string ())
|
|
{
|
|
return Methods(new EventSignalFuncImpl<X, tl::event<A1, A2, A3, A4>, type_pair_t<A1, type_pair_t<A2, type_pair_t<A3, type_pair_t<A4, empty_list_t> > > > > (event, name, doc));
|
|
}
|
|
|
|
// ...
|
|
template <class X, class A1, class A2, class A3, class A4, class S1, class S2, class S3, class S4>
|
|
inline Methods event_ext (const std::string &name, tl::event<A1, A2, A3, A4> &(*event) (X *),
|
|
const ArgSpec<S1> &s1, const ArgSpec<S2> &s2, const ArgSpec<S3> &s3, const ArgSpec<S4> &s4, const std::string &doc = std::string ())
|
|
{
|
|
return Methods((new EventSignalFuncImpl<X, tl::event<A1, A2, A3, A4>, type_pair_t<A1, type_pair_t<A2, type_pair_t<A3, type_pair_t<A4, empty_list_t> > > > > (event, name, doc))
|
|
->def_arg (s1)->def_arg (s2)->def_arg (s3)->def_arg (s4));
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|