RTBKit  0.9
Open-source framework to create real-time ad bidding systems.
soa/sigslot/slot.h
00001 /* slot.h                                                          -*- C++ -*-
00002    Jeremy Barnes, 16 November 2010
00003    Copyright (c) 2010 Datacratic.  All rights reserved.
00004 
00005    Implementation of a "slot" object that can be attached to a signal.
00006 */
00007 
00008 #pragma once
00009 
00010 #include <boost/function.hpp>
00011 #include "jml/arch/demangle.h"
00012 #include "jml/arch/exception.h"
00013 #include "soa/js/js_call_fwd.h"
00014 
00015 namespace boost {
00016 namespace signals2 {
00017 struct connection;
00018 } // namespace signals2
00019 } // namespace boost
00020 
00021 namespace Json {
00022 struct Value;
00023 } // namespace Json
00024 
00025 namespace Datacratic {
00026 
00027 bool inJsContext();
00028 
00029 void enterJs(void * & locker);
00030 void exitJs(void * & locker);
00031 
00032 struct JSLocker {
00033     JSLocker()
00034     {
00035         enterJs(locker);
00036     }
00037     
00038     ~JSLocker()
00039     {
00040         exitJs(locker);
00041     }
00042     
00043     void * locker;
00044 };
00045 
00047 struct SlotDisconnector
00048     : public boost::function<void (void)> {
00049     
00050     template<typename T>
00051     SlotDisconnector(const T & f)
00052         : boost::function<void (void)>(f)
00053     {
00054     }
00055 
00056     SlotDisconnector()
00057     {
00058     }
00059 
00060     SlotDisconnector(const boost::signals2::connection & connection);
00061 };
00062 
00063 /* What do we want to do here?
00064    
00065    We want to be able to:
00066    - Pass boost::function objects of different types around through the one
00067      interface
00068    - Call them as a specific type of boost::function when we need to
00069    - Have them throw an exception if we try to call them as something that
00070      they're not
00071    - Be able to initialize them from javascript functions in such a way that
00072      everything will be forwarded automatically
00073    - Be able to make them callable from javascript
00074 */
00075 
00086 typedef void * (* Operations) (int op, void * arg);
00087 
00089 template<typename Fn>
00090 struct FunctionOps {
00091     typedef typename boost::function<Fn> Function;
00092 
00093     static void * ops(int op, void * arg)
00094     {
00095         switch (op) {
00096         case 0: *(const std::type_info **)(arg) = &typeid(Fn);  break;
00097         case 1: delete (boost::function<Fn> *)arg;  break;
00098         case 3: return new Function(*(boost::function<Fn> *)arg);
00099         default:
00100             throw ML::Exception("invalid operation number");
00101         }
00102         return 0;
00103     }
00104 };
00105 
00106 
00107 /*****************************************************************************/
00108 /* SLOT                                                                      */
00109 /*****************************************************************************/
00110 
00126 struct Slot {
00127     Slot()
00128         : fntype(EMPTY)
00129     {
00130     }
00131 
00132     Slot(Slot && other);
00133 
00134     Slot & operator = (Slot && other);
00135 
00136     void swap(Slot & other);
00137 
00138     Slot(const Slot & other);
00139 
00140     Slot & operator = (const Slot & other);
00141 
00142     // Initialize from a boost::function where the function type and the
00143     // called type are identical
00144     template<typename Fn, typename F>
00145     static Slot fromF(const boost::function<F> & fn,
00146                       Operations ops = FunctionOps<Fn>::ops,
00147                       JS::JSOperations jsops = JS::getOps(typeid(Fn)))
00148     {
00149 
00150         Slot result;
00151         if (fn.empty()) return result;
00152         result.fntype = BOOST;
00153         result.ops = ops;
00154         result.jsops = jsops;
00155         result.fn = new boost::function<Fn>(fn);
00156         return result;
00157     }
00158 
00159     // Initialize from a boost::function where the function type and the
00160     // called type are identical
00161     template<typename Fn, typename F>
00162     static Slot fromF(F fn,
00163                       Operations ops = FunctionOps<Fn>::ops,
00164                       JS::JSOperations jsops = JS::getOps(typeid(Fn)))
00165     {
00166         Slot result;
00167         //if (fn.empty()) return result;
00168         result.fntype = BOOST;
00169         result.ops = ops;
00170         result.jsops = jsops;
00171         result.fn = new boost::function<Fn>(fn);
00172         return result;
00173     }
00174                  
00175     template<typename Fn>
00176     Slot(const boost::function<Fn> & fn,
00177          Operations ops = FunctionOps<Fn>::ops,
00178          JS::JSOperations jsops = JS::getOps(typeid(Fn)))
00179         : fn(new boost::function<Fn>(fn)), ops(ops), jsops(jsops), fntype(BOOST)
00180     {
00181         if (!fn) {
00182             Slot new_me;
00183             swap(new_me);
00184         }
00185     }
00186     
00188     Slot(const v8::Handle<v8::Function> & fn);
00189 
00191     Slot(const v8::Handle<v8::Value> & fn);
00192 
00193     ~Slot();
00194 
00196     std::string print() const;
00197 
00199     bool isEmpty() const { return fntype == EMPTY; }
00200 
00202     template<typename CallAs, typename... Args>
00203     typename boost::function<CallAs>::result_type
00204     call(Args... args) const
00205     {
00206         if (!inJsContext())
00207             throw ML::Exception("callback outside JS context");
00208         return as<CallAs>()(args...);
00209     }
00210 
00214     v8::Handle<v8::Value> call(const v8::Arguments & args) const;
00215     v8::Handle<v8::Value> call(const v8::Handle<v8::Object> & This,
00216                                int argc, v8::Handle<v8::Value> argv[]) const;
00217 
00224     v8::Local<v8::Function> toJsFunction() const;
00225 
00229     const std::type_info & type() const;
00230     
00234     template<typename Fn>
00235     boost::function<Fn>
00236     as(JS::JSOperations jsops = 0) const
00237     {
00238         switch (fntype) {
00239         case EMPTY:
00240             return boost::function<Fn>();
00241             //throw ML::Exception("can't convert empty notification");
00242         case BOOST:
00243             if (typeid(Fn) != type())
00244                 throw ML::Exception("couldn't convert function of type "
00245                                     + ML::demangle(type()) + " to type "
00246                                     + ML::type_name<Fn>());
00247         
00248             return static_cast<const boost::function<Fn> & >(*fn);
00249 
00250         case JS: {
00251             if (!inJsContext())
00252                 throw ML::Exception("callback outside JS context");
00253 
00254             if (!jsops)
00255                 jsops = JS::getOps(typeid(Fn));
00256 
00257             JS::JSAsBoost op = (JS::JSAsBoost)jsops;
00258 
00259             boost::function<Fn> result;
00260             v8::Handle<v8::Object> * This = 0;
00261 
00262             op(1, *jsfn, *This, result);
00263 
00264             return result;
00265         }
00266         default:
00267             throw ML::Exception("unknown operation type");
00268         }
00269     }
00270 
00272     template<typename Fn>
00273     operator boost::function<Fn> () const
00274     {
00275         return as<Fn>();
00276     }
00277 
00278     template<typename R>
00279     operator boost::function0<R> () const
00280     {
00281         return as<R ()>();
00282     }
00283     
00284 private:
00285     union {
00286         // boost::function contents
00287         struct {
00288             boost::function_base * fn;
00289             Operations ops;
00290             JS::JSOperations jsops;
00291         };
00292         // JS function contents
00293         struct {
00294             v8::Persistent<v8::Function> * jsfn;
00295         };
00296     };
00297 
00299     enum FnType {
00300         EMPTY,   
00301         BOOST,   
00302         JS       
00303     };
00304 
00305     FnType fntype;  // TODO: find a way to do this without a member
00306 
00308     void free();
00309 };
00310 
00311 template<typename Fn, typename F>
00312 Slot slot(const F & fn,
00313           Operations ops = FunctionOps<Fn>::ops,
00314           JS::JSOperations jsops = JS::getOps(typeid(Fn)))
00315 {
00316     return Slot::fromF<Fn>(boost::function<Fn>(fn), ops, jsops);
00317 }
00318 
00319 template<typename Fn>
00320 struct SlotT : public Slot {
00321 
00322     SlotT()
00323     {
00324     }
00325 
00326     SlotT(const Slot & slot)
00327         : Slot(slot)
00328     {
00329     }
00330 
00331     SlotT(const boost::function<Fn> & fn,
00332          Operations ops = FunctionOps<Fn>::ops,
00333          JS::JSOperations jsops = JS::getOps(typeid(Fn)))
00334         : Slot(fn, ops, jsops)
00335     {
00336     }
00337     
00339     SlotT(const v8::Handle<v8::Function> & fn)
00340         : Slot(fn)
00341     {
00342     }
00343 
00346     SlotT(const v8::Handle<v8::Value> & fn)
00347         : Slot(fn)
00348     {
00349     }
00350 
00352     std::string print() const;
00353 
00355     template<typename... Args>
00356     typename boost::function<Fn>::result_type
00357     call(Args... args) const
00358     {
00359         return toBoost()(args...);
00360     }
00361 
00362     template<typename... Args>
00363     typename boost::function<Fn>::result_type
00364     operator () (Args... args) const
00365     {
00366         return call(args...);
00367     }
00368 
00369     using Slot::call;
00370     
00374     boost::function<Fn>
00375     toBoost(JS::JSOperations jsops = 0) const
00376     {
00377         return Slot::as<Fn>(jsops);
00378     }
00379 
00381     operator boost::function<Fn> () const
00382     {
00383         return as<Fn>();
00384     }
00385 
00389     std::function<Fn>
00390     toStd(JS::JSOperations jsops = 0) const
00391     {
00392         return Slot::as<Fn>(jsops);
00393     }
00394 
00396     operator std::function<Fn> () const
00397     {
00398         return as<Fn>();
00399     }
00400 };
00401 
00402 namespace JS {
00403 
00404 struct JSValue;
00405 
00406 Slot from_js(const JSValue &, Slot * = 0);
00407 
00408 template<typename Fn>
00409 SlotT<Fn> from_js(const JSValue & val, SlotT<Fn> * = 0)
00410 {
00411     return SlotT<Fn>(from_js(val, (Slot *)0));
00412 }
00413 
00414 template<typename Fn>
00415 boost::function<Fn> from_js(const JSValue & val, boost::function<Fn> * = 0)
00416 {
00417     return SlotT<Fn>(from_js(val, (Slot *)0)).toBoost();
00418 }
00419 
00420 template<typename Fn>
00421 boost::function<Fn> from_js_ref(const JSValue & val, boost::function<Fn> * = 0)
00422 {
00423     return SlotT<Fn>(from_js(val, (Slot *)0)).toBoost();
00424 }
00425 
00426 template<typename Fn>
00427 std::function<Fn> from_js(const JSValue & val, std::function<Fn> * = 0)
00428 {
00429     return SlotT<Fn>(from_js(val, (Slot *)0)).toBoost();
00430 }
00431 
00432 template<typename Fn>
00433 std::function<Fn> from_js_ref(const JSValue & val, std::function<Fn> * = 0)
00434 {
00435     return SlotT<Fn>(from_js(val, (Slot *)0)).toBoost();
00436 }
00437 
00438 void to_js(JSValue & val, const Slot & slot);
00439 
00440 template<typename Fn>
00441 void to_js(JSValue & val, const SlotT<Fn> & slot)
00442 {
00443     return to_js(val, (const Slot &)slot);
00444 }
00445 
00446 } // namespace JS
00447 } // namespace Datacratic
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator