![]() |
RTBKit
0.9
Open-source framework to create real-time ad bidding systems.
|
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