mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-10 16:46:35 +01:00
Make SignalN template variadic
This commit is contained in:
parent
6c14a6c407
commit
16dd8528c8
3 changed files with 537 additions and 361 deletions
|
|
@ -57,6 +57,10 @@ namespace PBD {
|
||||||
|
|
||||||
class LIBPBD_API Connection;
|
class LIBPBD_API Connection;
|
||||||
|
|
||||||
|
class LIBPBD_API ScopedConnection;
|
||||||
|
|
||||||
|
class LIBPBD_API ScopedConnectionList;
|
||||||
|
|
||||||
class LIBPBD_API SignalBase
|
class LIBPBD_API SignalBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -80,6 +84,202 @@ protected:
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename R>
|
||||||
|
class /*LIBPBD_API*/ OptionalLastValue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef boost::optional<R> result_type;
|
||||||
|
|
||||||
|
template <typename Iter>
|
||||||
|
result_type operator() (Iter first, Iter last) const {
|
||||||
|
result_type r;
|
||||||
|
while (first != last) {
|
||||||
|
r = *first;
|
||||||
|
++first;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Combiner, typename _Signature>
|
||||||
|
class SignalWithCombiner;
|
||||||
|
|
||||||
|
template <typename Combiner, typename R, typename... A>
|
||||||
|
class SignalWithCombiner<Combiner, R(A...)> : public SignalBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
typedef boost::function<R(A...)> slot_function_type;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/** The slots that this signal will call on emission */
|
||||||
|
typedef std::map<std::shared_ptr<Connection>, slot_function_type> Slots;
|
||||||
|
Slots _slots;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
static void compositor (typename boost::function<void(A...)> f,
|
||||||
|
EventLoop* event_loop,
|
||||||
|
EventLoop::InvalidationRecord* ir, A... a);
|
||||||
|
|
||||||
|
~SignalWithCombiner ();
|
||||||
|
|
||||||
|
void connect_same_thread (ScopedConnection& c, const slot_function_type& slot);
|
||||||
|
void connect_same_thread (ScopedConnectionList& clist, const slot_function_type& slot);
|
||||||
|
void connect (ScopedConnectionList& clist,
|
||||||
|
PBD::EventLoop::InvalidationRecord* ir,
|
||||||
|
const slot_function_type& slot,
|
||||||
|
PBD::EventLoop* event_loop);
|
||||||
|
void connect (ScopedConnection& c,
|
||||||
|
PBD::EventLoop::InvalidationRecord* ir,
|
||||||
|
const slot_function_type& slot,
|
||||||
|
PBD::EventLoop* event_loop);
|
||||||
|
|
||||||
|
typename Combiner::result_type operator() (A... a);
|
||||||
|
|
||||||
|
bool empty () const {
|
||||||
|
Glib::Threads::Mutex::Lock lm (_mutex);
|
||||||
|
return _slots.empty ();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size () const {
|
||||||
|
Glib::Threads::Mutex::Lock lm (_mutex);
|
||||||
|
return _slots.size ();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
friend class Connection;
|
||||||
|
|
||||||
|
std::shared_ptr<Connection> _connect (PBD::EventLoop::InvalidationRecord* ir, slot_function_type f);
|
||||||
|
void disconnect (std::shared_ptr<Connection> c);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Combiner, typename... A>
|
||||||
|
class SignalWithCombiner<Combiner, void(A...)> : public SignalBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
typedef boost::function<void(A...)> slot_function_type;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/** The slots that this signal will call on emission */
|
||||||
|
typedef std::map<std::shared_ptr<Connection>, slot_function_type> Slots;
|
||||||
|
Slots _slots;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
static void compositor (typename boost::function<void(A...)> f,
|
||||||
|
EventLoop* event_loop,
|
||||||
|
EventLoop::InvalidationRecord* ir, A... a);
|
||||||
|
|
||||||
|
~SignalWithCombiner ();
|
||||||
|
|
||||||
|
void connect_same_thread (ScopedConnection& c, const slot_function_type& slot);
|
||||||
|
void connect_same_thread (ScopedConnectionList& clist, const slot_function_type& slot);
|
||||||
|
void connect (ScopedConnectionList& clist,
|
||||||
|
PBD::EventLoop::InvalidationRecord* ir,
|
||||||
|
const slot_function_type& slot,
|
||||||
|
PBD::EventLoop* event_loop);
|
||||||
|
void connect (ScopedConnection& c,
|
||||||
|
PBD::EventLoop::InvalidationRecord* ir,
|
||||||
|
const slot_function_type& slot,
|
||||||
|
PBD::EventLoop* event_loop);
|
||||||
|
void operator() (A... a);
|
||||||
|
|
||||||
|
bool empty () const {
|
||||||
|
Glib::Threads::Mutex::Lock lm (_mutex);
|
||||||
|
return _slots.empty ();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size () const {
|
||||||
|
Glib::Threads::Mutex::Lock lm (_mutex);
|
||||||
|
return _slots.size ();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
friend class Connection;
|
||||||
|
|
||||||
|
std::shared_ptr<Connection> _connect (PBD::EventLoop::InvalidationRecord* ir,
|
||||||
|
slot_function_type f);
|
||||||
|
void disconnect (std::shared_ptr<Connection> c);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename R>
|
||||||
|
using DefaultCombiner = OptionalLastValue<R>;
|
||||||
|
|
||||||
|
template <typename _Signature>
|
||||||
|
class Signal;
|
||||||
|
|
||||||
|
template <typename R, typename... A>
|
||||||
|
class Signal<R(A...)> : public SignalWithCombiner<DefaultCombiner<R>, R(A...)> {};
|
||||||
|
|
||||||
|
template <typename R, typename C = DefaultCombiner<R>>
|
||||||
|
class
|
||||||
|
#ifndef NDEBUG
|
||||||
|
[[deprecated("Use Signal<R(A...)> or SignalWithCombiner<C, R(A...)> if you need a specific combiner instead.")]]
|
||||||
|
#endif
|
||||||
|
Signal0 : public SignalWithCombiner<C, R()> {};
|
||||||
|
|
||||||
|
template <typename R>
|
||||||
|
class Signal0<R> : public Signal<R()> {};
|
||||||
|
|
||||||
|
template <typename R, typename A1, typename C = DefaultCombiner<R>>
|
||||||
|
class
|
||||||
|
#ifndef NDEBUG
|
||||||
|
[[deprecated("Use Signal<R(A...)> or SignalWithCombiner<C, R(A...)> if you need a specific combiner instead.")]]
|
||||||
|
#endif
|
||||||
|
Signal1 : public SignalWithCombiner<C, R(A1)> {};
|
||||||
|
|
||||||
|
template <typename R, typename A1>
|
||||||
|
class Signal1<R, A1> : public Signal<R(A1)> {};
|
||||||
|
|
||||||
|
template <typename R, typename A1, typename A2, typename C = DefaultCombiner<R>>
|
||||||
|
class
|
||||||
|
#ifndef NDEBUG
|
||||||
|
[[deprecated("Use Signal<R(A...)> or SignalWithCombiner<C, R(A...)> if you need a specific combiner instead.")]]
|
||||||
|
#endif
|
||||||
|
Signal2 : public SignalWithCombiner<C, R(A1, A2)> {};
|
||||||
|
|
||||||
|
template <typename R, typename A1, typename A2>
|
||||||
|
class Signal2<R, A1, A2> : public Signal<R(A1, A2)> {};
|
||||||
|
|
||||||
|
template <typename R, typename A1, typename A2, typename A3, typename C = DefaultCombiner<R>>
|
||||||
|
class
|
||||||
|
#ifndef NDEBUG
|
||||||
|
[[deprecated("Use Signal<R(A...)> or SignalWithCombiner<C, R(A...)> if you need a specific combiner instead.")]]
|
||||||
|
#endif
|
||||||
|
Signal3 : public SignalWithCombiner<C, R(A1, A2, A3)> {};
|
||||||
|
|
||||||
|
template <typename R, typename A1, typename A2, typename A3>
|
||||||
|
class Signal3<R, A1, A2, A3> : public Signal<R(A1, A2, A3)> {};
|
||||||
|
|
||||||
|
template <typename R, typename A1, typename A2, typename A3, typename A4, typename C = DefaultCombiner<R>>
|
||||||
|
class
|
||||||
|
#ifndef NDEBUG
|
||||||
|
[[deprecated("Use Signal<R(A...)> or SignalWithCombiner<C, R(A...)> if you need a specific combiner instead.")]]
|
||||||
|
#endif
|
||||||
|
Signal4 : public SignalWithCombiner<C, R(A1, A2, A3, A4)> {};
|
||||||
|
|
||||||
|
template <typename R, typename A1, typename A2, typename A3, typename A4>
|
||||||
|
class Signal4<R, A1, A2, A3, A4> : public Signal<R(A1, A2, A3, A4)> {};
|
||||||
|
|
||||||
|
template <typename R, typename A1, typename A2, typename A3, typename A4, typename A5, typename C = DefaultCombiner<R>>
|
||||||
|
class
|
||||||
|
#ifndef NDEBUG
|
||||||
|
[[deprecated("Use Signal<R(A...)> or SignalWithCombiner<C, R(A...)> if you need a specific combiner instead.")]]
|
||||||
|
#endif
|
||||||
|
Signal5 : public SignalWithCombiner<C, R(A1, A2, A3, A4, A5)> {};
|
||||||
|
|
||||||
|
template <typename R, typename A1, typename A2, typename A3, typename A4, typename A5>
|
||||||
|
class Signal5<R, A1, A2, A3, A4, A5> : public Signal<R(A1, A2, A3, A4, A5)> {};
|
||||||
|
|
||||||
class LIBPBD_API Connection : public std::enable_shared_from_this<Connection>
|
class LIBPBD_API Connection : public std::enable_shared_from_this<Connection>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -139,24 +339,6 @@ private:
|
||||||
PBD::EventLoop::InvalidationRecord* _invalidation_record;
|
PBD::EventLoop::InvalidationRecord* _invalidation_record;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename R>
|
|
||||||
class /*LIBPBD_API*/ OptionalLastValue
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
typedef boost::optional<R> result_type;
|
|
||||||
|
|
||||||
template <typename Iter>
|
|
||||||
result_type operator() (Iter first, Iter last) const {
|
|
||||||
result_type r;
|
|
||||||
while (first != last) {
|
|
||||||
r = *first;
|
|
||||||
++first;
|
|
||||||
}
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef std::shared_ptr<Connection> UnscopedConnection;
|
typedef std::shared_ptr<Connection> UnscopedConnection;
|
||||||
|
|
||||||
class LIBPBD_API ScopedConnection
|
class LIBPBD_API ScopedConnection
|
||||||
|
|
@ -222,7 +404,343 @@ class LIBPBD_API ScopedConnectionList : public boost::noncopyable
|
||||||
ConnectionList _scoped_connection_list;
|
ConnectionList _scoped_connection_list;
|
||||||
};
|
};
|
||||||
|
|
||||||
#include "pbd/signals_generated.h"
|
template <typename Combiner, typename R, typename... A>
|
||||||
|
void
|
||||||
|
SignalWithCombiner<Combiner, R(A...)>::compositor (typename boost::function<void(A...)> f,
|
||||||
|
EventLoop* event_loop,
|
||||||
|
EventLoop::InvalidationRecord* ir, A... a)
|
||||||
|
{
|
||||||
|
event_loop->call_slot (ir, boost::bind (f, a...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Combiner, typename... A>
|
||||||
|
void
|
||||||
|
SignalWithCombiner<Combiner, void(A...)>::compositor (typename boost::function<void(A...)> f,
|
||||||
|
EventLoop* event_loop,
|
||||||
|
EventLoop::InvalidationRecord* ir, A... a)
|
||||||
|
{
|
||||||
|
event_loop->call_slot (ir, boost::bind (f, a...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Combiner, typename R, typename... A>
|
||||||
|
SignalWithCombiner<Combiner, R(A...)>::~SignalWithCombiner ()
|
||||||
|
{
|
||||||
|
_in_dtor.store (true, std::memory_order_release);
|
||||||
|
Glib::Threads::Mutex::Lock lm (_mutex);
|
||||||
|
/* Tell our connection objects that we are going away, so they don't try to call us */
|
||||||
|
for (typename Slots::const_iterator i = _slots.begin(); i != _slots.end(); ++i) {
|
||||||
|
i->first->signal_going_away ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Combiner, typename... A>
|
||||||
|
SignalWithCombiner<Combiner, void(A...)>::~SignalWithCombiner ()
|
||||||
|
{
|
||||||
|
_in_dtor.store (true, std::memory_order_release);
|
||||||
|
Glib::Threads::Mutex::Lock lm (_mutex);
|
||||||
|
/* Tell our connection objects that we are going away, so they don't try to call us */
|
||||||
|
for (typename Slots::const_iterator i = _slots.begin(); i != _slots.end(); ++i) {
|
||||||
|
i->first->signal_going_away ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Arrange for @a slot to be executed whenever this signal is emitted.
|
||||||
|
* Store the connection that represents this arrangement in @a c.
|
||||||
|
*
|
||||||
|
* NOTE: @a slot will be executed in the same thread that the signal is
|
||||||
|
* emitted in.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template <typename Combiner, typename R, typename... A>
|
||||||
|
void
|
||||||
|
SignalWithCombiner<Combiner, R(A...)>::connect_same_thread (ScopedConnection& c,
|
||||||
|
const slot_function_type& slot)
|
||||||
|
{
|
||||||
|
c = _connect (0, slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Combiner, typename... A>
|
||||||
|
void
|
||||||
|
SignalWithCombiner<Combiner, void(A...)>::connect_same_thread (ScopedConnection& c,
|
||||||
|
const slot_function_type& slot)
|
||||||
|
{
|
||||||
|
c = _connect (0, slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Arrange for @a slot to be executed whenever this signal is emitted.
|
||||||
|
* Add the connection that represents this arrangement to @a clist.
|
||||||
|
*
|
||||||
|
* NOTE: @a slot will be executed in the same thread that the signal is
|
||||||
|
* emitted in.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template <typename Combiner, typename R, typename... A>
|
||||||
|
void
|
||||||
|
SignalWithCombiner<Combiner, R(A...)>::connect_same_thread (ScopedConnectionList& clist,
|
||||||
|
const slot_function_type& slot)
|
||||||
|
{
|
||||||
|
clist.add_connection (_connect (0, slot));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Combiner, typename... A>
|
||||||
|
void
|
||||||
|
SignalWithCombiner<Combiner, void(A...)>::connect_same_thread (ScopedConnectionList& clist,
|
||||||
|
const slot_function_type& slot)
|
||||||
|
{
|
||||||
|
clist.add_connection (_connect (0, slot));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Arrange for @a slot to be executed in the context of @a event_loop
|
||||||
|
* whenever this signal is emitted. Add the connection that represents
|
||||||
|
* this arrangement to @a clist.
|
||||||
|
*
|
||||||
|
* If the event loop/thread in which @a slot will be executed will
|
||||||
|
* outlive the lifetime of any object referenced in @a slot,
|
||||||
|
* then an InvalidationRecord should be passed, allowing
|
||||||
|
* any request sent to the @a event_loop and not executed
|
||||||
|
* before the object is destroyed to be marked invalid.
|
||||||
|
*
|
||||||
|
* "outliving the lifetime" doesn't have a specific, detailed meaning,
|
||||||
|
* but is best illustrated by two contrasting examples:
|
||||||
|
*
|
||||||
|
* 1) the main GUI event loop/thread - this will outlive more or
|
||||||
|
* less all objects in the application, and thus when arranging for
|
||||||
|
* @a slot to be called in that context, an invalidation record is
|
||||||
|
* highly advisable.
|
||||||
|
*
|
||||||
|
* 2) a secondary event loop/thread which will be destroyed along
|
||||||
|
* with the objects that are typically referenced by @a slot.
|
||||||
|
* Assuming that the event loop is stopped before the objects are
|
||||||
|
* destroyed, there is no reason to pass in an invalidation record,
|
||||||
|
* and MISSING_INVALIDATOR may be used.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template <typename Combiner, typename R, typename... A>
|
||||||
|
void
|
||||||
|
SignalWithCombiner<Combiner, R(A...)>::connect (ScopedConnectionList& clist,
|
||||||
|
PBD::EventLoop::InvalidationRecord* ir,
|
||||||
|
const slot_function_type& slot,
|
||||||
|
PBD::EventLoop* event_loop)
|
||||||
|
{
|
||||||
|
if (ir) {
|
||||||
|
ir->event_loop = event_loop;
|
||||||
|
}
|
||||||
|
|
||||||
|
clist.add_connection (_connect (ir, [slot, event_loop, ir](A... a) {
|
||||||
|
return compositor(slot, event_loop, ir, a...);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Combiner, typename... A>
|
||||||
|
void
|
||||||
|
SignalWithCombiner<Combiner, void(A...)>::connect (ScopedConnectionList& clist,
|
||||||
|
PBD::EventLoop::InvalidationRecord* ir,
|
||||||
|
const slot_function_type& slot,
|
||||||
|
PBD::EventLoop* event_loop)
|
||||||
|
{
|
||||||
|
if (ir) {
|
||||||
|
ir->event_loop = event_loop;
|
||||||
|
}
|
||||||
|
|
||||||
|
clist.add_connection (_connect (ir, [slot, event_loop, ir](A... a) {
|
||||||
|
return compositor(slot, event_loop, ir, a...);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** See notes for the ScopedConnectionList variant of this function. This
|
||||||
|
* differs in that it stores the connection to the signal in a single
|
||||||
|
* ScopedConnection rather than a ScopedConnectionList.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template <typename Combiner, typename R, typename... A>
|
||||||
|
void
|
||||||
|
SignalWithCombiner<Combiner, R(A...)>::connect (ScopedConnection& c,
|
||||||
|
PBD::EventLoop::InvalidationRecord* ir,
|
||||||
|
const slot_function_type& slot,
|
||||||
|
PBD::EventLoop* event_loop)
|
||||||
|
{
|
||||||
|
if (ir) {
|
||||||
|
ir->event_loop = event_loop;
|
||||||
|
}
|
||||||
|
|
||||||
|
c = _connect (ir, [slot, event_loop, ir](A... a) {
|
||||||
|
return compositor(slot, event_loop, ir, a...);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Combiner, typename... A>
|
||||||
|
void
|
||||||
|
SignalWithCombiner<Combiner, void(A...)>::connect (ScopedConnection& c,
|
||||||
|
PBD::EventLoop::InvalidationRecord* ir,
|
||||||
|
const slot_function_type& slot,
|
||||||
|
PBD::EventLoop* event_loop)
|
||||||
|
{
|
||||||
|
if (ir) {
|
||||||
|
ir->event_loop = event_loop;
|
||||||
|
}
|
||||||
|
|
||||||
|
c = _connect (ir, [slot, event_loop, ir](A... a) {
|
||||||
|
return compositor(slot, event_loop, ir, a...);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Emit this signal. This will cause all slots connected to it be executed
|
||||||
|
* in the order that they were connected (cross-thread issues may alter
|
||||||
|
* the precise execution time of cross-thread slots).
|
||||||
|
*/
|
||||||
|
|
||||||
|
template <typename Combiner, typename R, typename... A>
|
||||||
|
typename Combiner::result_type
|
||||||
|
SignalWithCombiner<Combiner, R(A...)>::operator() (A... a)
|
||||||
|
{
|
||||||
|
/* First, take a copy of our list of slots as it is now */
|
||||||
|
|
||||||
|
Slots s;
|
||||||
|
{
|
||||||
|
Glib::Threads::Mutex::Lock lm (_mutex);
|
||||||
|
s = _slots;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<R> r;
|
||||||
|
for (typename Slots::const_iterator i = s.begin(); i != s.end(); ++i) {
|
||||||
|
|
||||||
|
/* We may have just called a slot, and this may have resulted in
|
||||||
|
* disconnection of other slots from us. The list copy means that
|
||||||
|
* this won't cause any problems with invalidated iterators, but we
|
||||||
|
* must check to see if the slot we are about to call is still on the list.
|
||||||
|
*/
|
||||||
|
bool still_there = false;
|
||||||
|
{
|
||||||
|
Glib::Threads::Mutex::Lock lm (_mutex);
|
||||||
|
still_there = _slots.find (i->first) != _slots.end ();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (still_there) {
|
||||||
|
r.push_back ((i->second)(a...));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Call our combiner to do whatever is required to the result values */
|
||||||
|
Combiner c;
|
||||||
|
return c (r.begin(), r.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Combiner, typename... A>
|
||||||
|
void
|
||||||
|
SignalWithCombiner<Combiner, void(A...)>::operator() (A... a)
|
||||||
|
{
|
||||||
|
/* First, take a copy of our list of slots as it is now */
|
||||||
|
|
||||||
|
Slots s;
|
||||||
|
{
|
||||||
|
Glib::Threads::Mutex::Lock lm (_mutex);
|
||||||
|
s = _slots;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (typename Slots::const_iterator i = s.begin(); i != s.end(); ++i) {
|
||||||
|
|
||||||
|
/* We may have just called a slot, and this may have resulted in
|
||||||
|
* disconnection of other slots from us. The list copy means that
|
||||||
|
* this won't cause any problems with invalidated iterators, but we
|
||||||
|
* must check to see if the slot we are about to call is still on the list.
|
||||||
|
*/
|
||||||
|
bool still_there = false;
|
||||||
|
{
|
||||||
|
Glib::Threads::Mutex::Lock lm (_mutex);
|
||||||
|
still_there = _slots.find (i->first) != _slots.end ();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (still_there) {
|
||||||
|
(i->second)(a...);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Combiner, typename R, typename... A>
|
||||||
|
std::shared_ptr<Connection>
|
||||||
|
SignalWithCombiner<Combiner, R(A...)>::_connect (PBD::EventLoop::InvalidationRecord* ir,
|
||||||
|
slot_function_type f)
|
||||||
|
{
|
||||||
|
std::shared_ptr<Connection> c (new Connection (this, ir));
|
||||||
|
Glib::Threads::Mutex::Lock lm (_mutex);
|
||||||
|
_slots[c] = f;
|
||||||
|
#ifdef DEBUG_PBD_SIGNAL_CONNECTIONS
|
||||||
|
if (_debug_connection) {
|
||||||
|
std::cerr << "+++++++ CONNECT " << this << " size now " << _slots.size() << std::endl;
|
||||||
|
stacktrace (std::cerr, 10);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Combiner, typename... A>
|
||||||
|
std::shared_ptr<Connection>
|
||||||
|
SignalWithCombiner<Combiner, void(A...)>::_connect (PBD::EventLoop::InvalidationRecord* ir,
|
||||||
|
slot_function_type f)
|
||||||
|
{
|
||||||
|
std::shared_ptr<Connection> c (new Connection (this, ir));
|
||||||
|
Glib::Threads::Mutex::Lock lm (_mutex);
|
||||||
|
_slots[c] = f;
|
||||||
|
#ifdef DEBUG_PBD_SIGNAL_CONNECTIONS
|
||||||
|
if (_debug_connection) {
|
||||||
|
std::cerr << "+++++++ CONNECT " << this << " size now " << _slots.size() << std::endl;
|
||||||
|
stacktrace (std::cerr, 10);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Combiner, typename R, typename... A>
|
||||||
|
void
|
||||||
|
SignalWithCombiner<Combiner, R(A...)>::disconnect (std::shared_ptr<Connection> c)
|
||||||
|
{
|
||||||
|
/* ~ScopedConnection can call this concurrently with our d'tor */
|
||||||
|
Glib::Threads::Mutex::Lock lm (_mutex, Glib::Threads::TRY_LOCK);
|
||||||
|
while (!lm.locked()) {
|
||||||
|
if (_in_dtor.load (std::memory_order_acquire)) {
|
||||||
|
/* d'tor signal_going_away() took care of everything already */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Spin */
|
||||||
|
lm.try_acquire ();
|
||||||
|
}
|
||||||
|
_slots.erase (c);
|
||||||
|
lm.release ();
|
||||||
|
|
||||||
|
c->disconnected ();
|
||||||
|
#ifdef DEBUG_PBD_SIGNAL_CONNECTIONS
|
||||||
|
if (_debug_connection) {
|
||||||
|
std::cerr << "------- DISCCONNECT " << this << " size now " << _slots.size() << std::endl;
|
||||||
|
stacktrace (std::cerr, 10);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Combiner, typename... A>
|
||||||
|
void
|
||||||
|
SignalWithCombiner<Combiner, void(A...)>::disconnect (std::shared_ptr<Connection> c)
|
||||||
|
{
|
||||||
|
/* ~ScopedConnection can call this concurrently with our d'tor */
|
||||||
|
Glib::Threads::Mutex::Lock lm (_mutex, Glib::Threads::TRY_LOCK);
|
||||||
|
while (!lm.locked()) {
|
||||||
|
if (_in_dtor.load (std::memory_order_acquire)) {
|
||||||
|
/* d'tor signal_going_away() took care of everything already */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Spin */
|
||||||
|
lm.try_acquire ();
|
||||||
|
}
|
||||||
|
_slots.erase (c);
|
||||||
|
lm.release ();
|
||||||
|
|
||||||
|
c->disconnected ();
|
||||||
|
#ifdef DEBUG_PBD_SIGNAL_CONNECTIONS
|
||||||
|
if (_debug_connection) {
|
||||||
|
std::cerr << "------- DISCCONNECT " << this << " size now " << _slots.size() << std::endl;
|
||||||
|
stacktrace (std::cerr, 10);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
} /* namespace */
|
} /* namespace */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,338 +0,0 @@
|
||||||
#!/usr/bin/python
|
|
||||||
#
|
|
||||||
# Copyright (C) 2009-2012 Paul Davis
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
|
|
||||||
#
|
|
||||||
# This file generates the header signals_generated.h, which
|
|
||||||
# will be put in build/libs/pbd/pbd by waf.
|
|
||||||
#
|
|
||||||
# It is probably easier to read build/libs/pbd/pbd/signals_generated.h
|
|
||||||
# than this if you want to read the code!
|
|
||||||
#
|
|
||||||
|
|
||||||
from __future__ import print_function
|
|
||||||
import sys
|
|
||||||
|
|
||||||
if len(sys.argv) < 2:
|
|
||||||
print('Syntax: %s <path>' % sys.argv[0])
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
f = open(sys.argv[1], 'w')
|
|
||||||
|
|
||||||
print("/** THIS FILE IS AUTOGENERATED by signals.py: CHANGES WILL BE LOST */\n", file=f)
|
|
||||||
|
|
||||||
# Produce a comma-separated string from a list of substrings,
|
|
||||||
# giving an optional prefix to each substring
|
|
||||||
def comma_separated(n, prefix = ""):
|
|
||||||
r = ""
|
|
||||||
for i in range(0, len(n)):
|
|
||||||
if i > 0:
|
|
||||||
r += ", "
|
|
||||||
r += "%s%s" % (prefix, n[i])
|
|
||||||
return r
|
|
||||||
|
|
||||||
# Generate one SignalN class definition
|
|
||||||
# @param f File to write to
|
|
||||||
# @param n Number of parameters
|
|
||||||
# @param v True to specialize the template for a void return type
|
|
||||||
def signal(f, n, v):
|
|
||||||
|
|
||||||
# The parameters in the form A1, A2, A3, ...
|
|
||||||
An = []
|
|
||||||
for i in range(0, n):
|
|
||||||
An.append("A%d" % (i + 1))
|
|
||||||
|
|
||||||
# The parameters in the form A1 a1, A2 a2, A3 a3, ...
|
|
||||||
Anan = []
|
|
||||||
for a in An:
|
|
||||||
Anan.append('%s %s' % (a, a.lower()))
|
|
||||||
|
|
||||||
# The parameters in the form a1, a2, a3, ...
|
|
||||||
an = []
|
|
||||||
for a in An:
|
|
||||||
an.append(a.lower())
|
|
||||||
|
|
||||||
# If the template is fully specialized, use of typename SomeTypedef::iterator is illegal
|
|
||||||
# in c++03 (should use just SomeTypedef::iterator) [although use of typename is ok in c++0x]
|
|
||||||
# http://stackoverflow.com/questions/6076015/typename-outside-of-template
|
|
||||||
if n == 0 and v:
|
|
||||||
typename = ""
|
|
||||||
else:
|
|
||||||
typename = "typename "
|
|
||||||
|
|
||||||
if v:
|
|
||||||
print("/** A signal with %d parameters (specialisation for a void return) */" % n, file=f)
|
|
||||||
else:
|
|
||||||
print("/** A signal with %d parameters */" % n, file=f)
|
|
||||||
if v:
|
|
||||||
print("template <%s>" % comma_separated(An, "typename "), file=f)
|
|
||||||
print("class Signal%d<%s> : public SignalBase" % (n, comma_separated(["void"] + An)), file=f)
|
|
||||||
else:
|
|
||||||
print("template <%s>" % comma_separated(["R"] + An + ["C = OptionalLastValue<R> "], "typename "), file=f)
|
|
||||||
print("class Signal%d : public SignalBase" % n, file=f)
|
|
||||||
|
|
||||||
print("{", file=f)
|
|
||||||
print("public:", file=f)
|
|
||||||
print("", file=f)
|
|
||||||
if v:
|
|
||||||
print("\ttypedef boost::function<void(%s)> slot_function_type;" % comma_separated(An), file=f)
|
|
||||||
print("\ttypedef void result_type;", file=f)
|
|
||||||
else:
|
|
||||||
print("\ttypedef boost::function<R(%s)> slot_function_type;" % comma_separated(An), file=f)
|
|
||||||
print("\ttypedef boost::optional<R> result_type;", file=f)
|
|
||||||
|
|
||||||
print("", file=f)
|
|
||||||
|
|
||||||
print("private:", file=f)
|
|
||||||
|
|
||||||
print("""
|
|
||||||
\t/** The slots that this signal will call on emission */
|
|
||||||
\ttypedef std::map<std::shared_ptr<Connection>, slot_function_type> Slots;
|
|
||||||
\tSlots _slots;
|
|
||||||
""", file=f)
|
|
||||||
|
|
||||||
print("public:", file=f)
|
|
||||||
print("", file=f)
|
|
||||||
print("\t~Signal%d () {" % n, file=f)
|
|
||||||
|
|
||||||
print("\t\t_in_dtor.store (true, std::memory_order_release);", file=f)
|
|
||||||
print("\t\tGlib::Threads::Mutex::Lock lm (_mutex);", file=f)
|
|
||||||
print("\t\t/* Tell our connection objects that we are going away, so they don't try to call us */", file=f)
|
|
||||||
print("\t\tfor (%sSlots::const_iterator i = _slots.begin(); i != _slots.end(); ++i) {" % typename, file=f)
|
|
||||||
|
|
||||||
print("\t\t\ti->first->signal_going_away ();", file=f)
|
|
||||||
print("\t\t}", file=f)
|
|
||||||
print("\t}", file=f)
|
|
||||||
print("", file=f)
|
|
||||||
|
|
||||||
if n == 0:
|
|
||||||
p = ""
|
|
||||||
q = ""
|
|
||||||
else:
|
|
||||||
p = ", %s" % comma_separated(Anan)
|
|
||||||
q = ", %s" % comma_separated(an)
|
|
||||||
|
|
||||||
print("\tstatic void compositor (%sboost::function<void(%s)> f, EventLoop* event_loop, EventLoop::InvalidationRecord* ir%s) {" % (typename, comma_separated(An), p), file=f)
|
|
||||||
print("\t\tevent_loop->call_slot (ir, boost::bind (f%s));" % q, file=f)
|
|
||||||
print("\t}", file=f)
|
|
||||||
|
|
||||||
print("""
|
|
||||||
\t/** Arrange for @a slot to be executed whenever this signal is emitted.
|
|
||||||
\t * Store the connection that represents this arrangement in @a c.
|
|
||||||
\t *
|
|
||||||
\t * NOTE: @a slot will be executed in the same thread that the signal is
|
|
||||||
\t * emitted in.
|
|
||||||
\t */
|
|
||||||
|
|
||||||
\tvoid connect_same_thread (ScopedConnection& c, const slot_function_type& slot) {
|
|
||||||
\t\tc = _connect (0, slot);
|
|
||||||
\t}
|
|
||||||
|
|
||||||
\t/** Arrange for @a slot to be executed whenever this signal is emitted.
|
|
||||||
\t * Add the connection that represents this arrangement to @a clist.
|
|
||||||
\t *
|
|
||||||
\t * NOTE: @a slot will be executed in the same thread that the signal is
|
|
||||||
\t * emitted in.
|
|
||||||
\t */
|
|
||||||
|
|
||||||
\tvoid connect_same_thread (ScopedConnectionList& clist, const slot_function_type& slot) {
|
|
||||||
\t\tclist.add_connection (_connect (0, slot));
|
|
||||||
\t}
|
|
||||||
|
|
||||||
\t/** Arrange for @a slot to be executed in the context of @a event_loop
|
|
||||||
\t * whenever this signal is emitted. Add the connection that represents
|
|
||||||
\t * this arrangement to @a clist.
|
|
||||||
\t *
|
|
||||||
\t * If the event loop/thread in which @a slot will be executed will
|
|
||||||
\t * outlive the lifetime of any object referenced in @a slot,
|
|
||||||
\t * then an InvalidationRecord should be passed, allowing
|
|
||||||
\t * any request sent to the @a event_loop and not executed
|
|
||||||
\t * before the object is destroyed to be marked invalid.
|
|
||||||
\t *
|
|
||||||
\t * "outliving the lifetime" doesn't have a specific, detailed meaning,
|
|
||||||
\t * but is best illustrated by two contrasting examples:
|
|
||||||
\t *
|
|
||||||
\t * 1) the main GUI event loop/thread - this will outlive more or
|
|
||||||
\t * less all objects in the application, and thus when arranging for
|
|
||||||
\t * @a slot to be called in that context, an invalidation record is
|
|
||||||
\t * highly advisable.
|
|
||||||
\t *
|
|
||||||
\t * 2) a secondary event loop/thread which will be destroyed along
|
|
||||||
\t * with the objects that are typically referenced by @a slot.
|
|
||||||
\t * Assuming that the event loop is stopped before the objects are
|
|
||||||
\t * destroyed, there is no reason to pass in an invalidation record,
|
|
||||||
\t * and MISSING_INVALIDATOR may be used.
|
|
||||||
\t */
|
|
||||||
|
|
||||||
\tvoid connect (ScopedConnectionList& clist,
|
|
||||||
\t PBD::EventLoop::InvalidationRecord* ir,
|
|
||||||
\t const slot_function_type& slot,
|
|
||||||
\t PBD::EventLoop* event_loop) {
|
|
||||||
|
|
||||||
\t\tif (ir) {
|
|
||||||
\t\t\tir->event_loop = event_loop;
|
|
||||||
\t\t}
|
|
||||||
""", file=f)
|
|
||||||
u = []
|
|
||||||
for i in range(0, n):
|
|
||||||
u.append("_%d" % (i + 1))
|
|
||||||
|
|
||||||
if n == 0:
|
|
||||||
p = ""
|
|
||||||
else:
|
|
||||||
p = ", %s" % comma_separated(u)
|
|
||||||
|
|
||||||
print("\t\tclist.add_connection (_connect (ir, boost::bind (&compositor, slot, event_loop, ir%s)));" % p, file=f)
|
|
||||||
|
|
||||||
print("""
|
|
||||||
\t}
|
|
||||||
|
|
||||||
\t/** See notes for the ScopedConnectionList variant of this function. This
|
|
||||||
\t * differs in that it stores the connection to the signal in a single
|
|
||||||
\t * ScopedConnection rather than a ScopedConnectionList.
|
|
||||||
\t */
|
|
||||||
|
|
||||||
\tvoid connect (ScopedConnection& c,
|
|
||||||
\t PBD::EventLoop::InvalidationRecord* ir,
|
|
||||||
\t const slot_function_type& slot,
|
|
||||||
\t PBD::EventLoop* event_loop) {
|
|
||||||
|
|
||||||
\t\tif (ir) {
|
|
||||||
\t\t\tir->event_loop = event_loop;
|
|
||||||
\t\t}
|
|
||||||
""", file=f)
|
|
||||||
print("\t\tc = _connect (ir, boost::bind (&compositor, slot, event_loop, ir%s));" % p, file=f)
|
|
||||||
print("\t}", file=f)
|
|
||||||
|
|
||||||
print("""
|
|
||||||
\t/** Emit this signal. This will cause all slots connected to it be executed
|
|
||||||
\t * in the order that they were connected (cross-thread issues may alter
|
|
||||||
\t * the precise execution time of cross-thread slots).
|
|
||||||
\t */
|
|
||||||
""", file=f)
|
|
||||||
|
|
||||||
if v:
|
|
||||||
print("\tvoid operator() (%s)" % comma_separated(Anan), file=f)
|
|
||||||
else:
|
|
||||||
print("\ttypename C::result_type operator() (%s)" % comma_separated(Anan), file=f)
|
|
||||||
print("\t{", file=f)
|
|
||||||
print("\t\t/* First, take a copy of our list of slots as it is now */", file=f)
|
|
||||||
print("", file=f)
|
|
||||||
print("\t\tSlots s;", file=f)
|
|
||||||
print("\t\t{", file=f)
|
|
||||||
print("\t\t\tGlib::Threads::Mutex::Lock lm (_mutex);", file=f)
|
|
||||||
print("\t\t\ts = _slots;", file=f)
|
|
||||||
print("\t\t}", file=f)
|
|
||||||
print("", file=f)
|
|
||||||
if not v:
|
|
||||||
print("\t\tstd::list<R> r;", file=f)
|
|
||||||
print("\t\tfor (%sSlots::const_iterator i = s.begin(); i != s.end(); ++i) {" % typename, file=f)
|
|
||||||
print("""
|
|
||||||
\t\t\t/* We may have just called a slot, and this may have resulted in
|
|
||||||
\t\t\t * disconnection of other slots from us. The list copy means that
|
|
||||||
\t\t\t * this won't cause any problems with invalidated iterators, but we
|
|
||||||
\t\t\t * must check to see if the slot we are about to call is still on the list.
|
|
||||||
\t\t\t */
|
|
||||||
\t\t\tbool still_there = false;
|
|
||||||
\t\t\t{
|
|
||||||
\t\t\t\tGlib::Threads::Mutex::Lock lm (_mutex);
|
|
||||||
\t\t\t\tstill_there = _slots.find (i->first) != _slots.end ();
|
|
||||||
\t\t\t}
|
|
||||||
|
|
||||||
\t\t\tif (still_there) {""", file=f)
|
|
||||||
if v:
|
|
||||||
print("\t\t\t\t(i->second)(%s);" % comma_separated(an), file=f)
|
|
||||||
else:
|
|
||||||
print("\t\t\t\tr.push_back ((i->second)(%s));" % comma_separated(an), file=f)
|
|
||||||
print("\t\t\t}", file=f)
|
|
||||||
print("\t\t}", file=f)
|
|
||||||
print("", file=f)
|
|
||||||
if not v:
|
|
||||||
print("\t\t/* Call our combiner to do whatever is required to the result values */", file=f)
|
|
||||||
print("\t\tC c;", file=f)
|
|
||||||
print("\t\treturn c (r.begin(), r.end());", file=f)
|
|
||||||
print("\t}", file=f)
|
|
||||||
|
|
||||||
print("""
|
|
||||||
\tbool empty () const {
|
|
||||||
\t\tGlib::Threads::Mutex::Lock lm (_mutex);
|
|
||||||
\t\treturn _slots.empty ();
|
|
||||||
\t}
|
|
||||||
""", file=f)
|
|
||||||
print("""
|
|
||||||
\tsize_t size () const {
|
|
||||||
\t\tGlib::Threads::Mutex::Lock lm (_mutex);
|
|
||||||
\t\treturn _slots.size ();
|
|
||||||
\t}
|
|
||||||
""", file=f)
|
|
||||||
|
|
||||||
if v:
|
|
||||||
tp = comma_separated(["void"] + An)
|
|
||||||
else:
|
|
||||||
tp = comma_separated(["R"] + An + ["C"])
|
|
||||||
|
|
||||||
print("private:", file=f)
|
|
||||||
print("", file=f)
|
|
||||||
print("\tfriend class Connection;", file=f)
|
|
||||||
|
|
||||||
print("""
|
|
||||||
\tstd::shared_ptr<Connection> _connect (PBD::EventLoop::InvalidationRecord* ir, slot_function_type f)
|
|
||||||
\t{
|
|
||||||
\t\tstd::shared_ptr<Connection> c (new Connection (this, ir));
|
|
||||||
\t\tGlib::Threads::Mutex::Lock lm (_mutex);
|
|
||||||
\t\t_slots[c] = f;
|
|
||||||
#ifdef DEBUG_PBD_SIGNAL_CONNECTIONS
|
|
||||||
\t\tif (_debug_connection) {
|
|
||||||
\t\t\tstd::cerr << "+++++++ CONNECT " << this << " size now " << _slots.size() << std::endl;
|
|
||||||
\t\t\tPBD::stacktrace (std::cerr, 10);
|
|
||||||
\t\t}
|
|
||||||
#endif
|
|
||||||
\t\treturn c;
|
|
||||||
\t}""", file=f)
|
|
||||||
|
|
||||||
print("""
|
|
||||||
\tvoid disconnect (std::shared_ptr<Connection> c)
|
|
||||||
\t{
|
|
||||||
\t\t/* ~ScopedConnection can call this concurrently with our d'tor */
|
|
||||||
\t\tGlib::Threads::Mutex::Lock lm (_mutex, Glib::Threads::TRY_LOCK);
|
|
||||||
\t\twhile (!lm.locked()) {
|
|
||||||
\t\t\tif (_in_dtor.load (std::memory_order_acquire)) {
|
|
||||||
\t\t\t/* d'tor signal_going_away() took care of everything already */
|
|
||||||
\t\t\t\treturn;
|
|
||||||
\t\t\t}
|
|
||||||
\t\t\t/* Spin */
|
|
||||||
\t\t\tlm.try_acquire ();
|
|
||||||
\t\t}
|
|
||||||
\t\t_slots.erase (c);
|
|
||||||
\t\tlm.release ();
|
|
||||||
|
|
||||||
\t\tc->disconnected ();
|
|
||||||
#ifdef DEBUG_PBD_SIGNAL_CONNECTIONS
|
|
||||||
\t\tif (_debug_connection) {
|
|
||||||
\t\t\tstd::cerr << "------- DISCCONNECT " << this << " size now " << _slots.size() << std::endl;
|
|
||||||
\t\t\tPBD::stacktrace (std::cerr, 10);
|
|
||||||
\t\t}
|
|
||||||
#endif
|
|
||||||
\t}
|
|
||||||
|
|
||||||
};
|
|
||||||
""", file=f)
|
|
||||||
|
|
||||||
for i in range(0, 6):
|
|
||||||
signal(f, i, False)
|
|
||||||
signal(f, i, True)
|
|
||||||
|
|
@ -143,9 +143,6 @@ def build(bld):
|
||||||
{'LIBPBD_VERSION' : LIBPBD_VERSION,
|
{'LIBPBD_VERSION' : LIBPBD_VERSION,
|
||||||
'LIBPBD_MAJOR_VERSION' : LIBPBD_MAJOR_VERSION})
|
'LIBPBD_MAJOR_VERSION' : LIBPBD_MAJOR_VERSION})
|
||||||
|
|
||||||
# Make signals_generated.h using signals.py
|
|
||||||
bld(rule = sys.executable + ' ${SRC} ${TGT}', source = 'pbd/signals.py', target = 'pbd/signals_generated.h', name="pbdsignals", features='use', ext_out=['.h'])
|
|
||||||
|
|
||||||
# Library
|
# Library
|
||||||
if bld.is_defined ('INTERNAL_SHARED_LIBS'):
|
if bld.is_defined ('INTERNAL_SHARED_LIBS'):
|
||||||
obj = bld.shlib(features = 'cxx cxxshlib', source=libpbd_sources)
|
obj = bld.shlib(features = 'cxx cxxshlib', source=libpbd_sources)
|
||||||
|
|
@ -163,7 +160,6 @@ def build(bld):
|
||||||
obj.includes = ['.']
|
obj.includes = ['.']
|
||||||
obj.name = 'libpbd'
|
obj.name = 'libpbd'
|
||||||
obj.target = 'pbd'
|
obj.target = 'pbd'
|
||||||
obj.use = 'pbdsignals'
|
|
||||||
obj.uselib = 'GLIBMM SIGCPP XML UUID SNDFILE GIOMM ARCHIVE CURL'
|
obj.uselib = 'GLIBMM SIGCPP XML UUID SNDFILE GIOMM ARCHIVE CURL'
|
||||||
if sys.platform == 'darwin':
|
if sys.platform == 'darwin':
|
||||||
TaskGen.task_gen.mappings['.mm'] = TaskGen.task_gen.mappings['.cc']
|
TaskGen.task_gen.mappings['.mm'] = TaskGen.task_gen.mappings['.cc']
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue