diff --git a/CMakeLists.txt b/CMakeLists.txt index d66b3903e..23bd8de62 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,8 @@ include(CMakeModules/pch.cmake) get_filename_component(AUTOWIRING_ROOT_DIR . ABSOLUTE) if(CMAKE_SOURCE_DIR STREQUAL AUTOWIRING_ROOT_DIR) set(AUTOWIRING_BUILD_TESTS_DEFAULT ON) + set(AUTOWIRING_BUILD_EXAMPLES_DEFAULT ON) + set(AUTOWIRING_BUILD_AUTONET_DEFAULT ON) # All of our binaries go to one place: The binaries output directory. We only want to tinker # with this if we're building by ourselves, otherwise we just do whatever the enclosing project @@ -33,6 +35,8 @@ if(CMAKE_SOURCE_DIR STREQUAL AUTOWIRING_ROOT_DIR) set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) else() set(AUTOWIRING_BUILD_TESTS_DEFAULT OFF) + set(AUTOWIRING_BUILD_EXAMPLES_DEFAULT OFF) + set(AUTOWIRING_BUILD_AUTONET_DEFAULT OFF) endif() option(AUTOWIRING_BUILD_TESTS "Build Autowiring unit tests" ${AUTOWIRING_BUILD_TESTS_DEFAULT}) @@ -53,7 +57,13 @@ include_directories( ) add_subdirectory(src) add_subdirectory(contrib) +# Build examples +option(AUTOWIRING_BUILD_EXAMPLES "Build Autowiring examples" ${AUTOWIRING_BUILD_EXAMPLES_DEFAULT}) +if(AUTOWIRING_BUILD_EXAMPLES) + add_subdirectory(examples) +endif() +# CMake configurations configure_file(autowiring-config.cmake.in autowiring-config.cmake @ONLY) configure_file(autowiring-configVersion.cmake.in autowiring-configVersion.cmake @ONLY) diff --git a/examples/AutoFilterExample.cpp b/examples/AutoFilterExample.cpp new file mode 100644 index 000000000..df63d3342 --- /dev/null +++ b/examples/AutoFilterExample.cpp @@ -0,0 +1,8 @@ +// Copyright (C) 2012-2014 Leap Motion, Inc. All rights reserved. +#include +#include +#include + +int main() { + std::cout << "Hello World" << std::endl; +} \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 000000000..a3b9058f4 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,15 @@ +add_executable(ContextExample ContextExample.cpp) +target_link_libraries(ContextExample Autowiring) +set_property(TARGET ContextExample PROPERTY FOLDER "Examples") + +add_executable(ThreadExample ThreadExample.cpp) +target_link_libraries(ThreadExample Autowiring) +set_property(TARGET ThreadExample PROPERTY FOLDER "Examples") + +add_executable(EventExample EventExample.cpp) +target_link_libraries(EventExample Autowiring) +set_property(TARGET EventExample PROPERTY FOLDER "Examples") + +add_executable(AutoFilterExample AutoFilterExample.cpp) +target_link_libraries(AutoFilterExample Autowiring) +set_property(TARGET AutoFilterExample PROPERTY FOLDER "Examples") diff --git a/examples/ContextExample.cpp b/examples/ContextExample.cpp new file mode 100644 index 000000000..eee67b8b1 --- /dev/null +++ b/examples/ContextExample.cpp @@ -0,0 +1,131 @@ +// Copyright (C) 2012-2014 Leap Motion, Inc. All rights reserved. +#include +#include +#include + +// pretty print boolean with desciption +#define check(desc,val) std::cout << desc << (val ? ": True" : ": False") << std::endl + +// dummy classes +class Foo { +public: + int x; +}; + +class Bar{ +public: + Autowired foo; +}; + +int main() { + /////////////////////////// + // // + // GlobalCoreContext // + // // + /////////////////////////// + { + // The CoreContext is the basic unit of organization in Autowriring. They are organized + // in a tree structure, the root of which is the GlobalCoreContext. Each thread + // keeps track of its current context, which defaults to GlobalCoreContext + + // This creates a shared_ptr to the global context + AutoGlobalContext global; + + // This is the a shared_ptr to the current context + AutoCurrentContext ctxt; + + // Since we default to the current context, they should be the same + check("Current context is global", global==ctxt); + } + + ///////////////////////////////////////////////// + // // + // Context Creation and Switching Contexts // + // // + ///////////////////////////////////////////////// + { + // New contexts can be created using the 'Create' factory method on a context. + // The factory will create a child context to 'this' context. + // A helper type 'AutoCreateContext' will call Create on the current context + // automatically + + AutoGlobalContext global; //same as AutoCurrentContext here + + // Create's a chile of the current context + AutoCreateContext childCtxt; // Same as childCtxt = AutoCurrentContext()->Create(); + + // Even though we've created a child context, we're still in the global context + check("Are we in the GlobalCoreContext", AutoCurrentContext()->IsGlobalContext()); + + // CurrentContextPusher can be used to switch contexts using RAII patterns + { + CurrentContextPusher pshr(childCtxt); + check("Now in child context", AutoCurrentContext() == childCtxt); + } + // Back in global context + } + + /////////////////////////////////////////////////////// + // // + // Adding members to a context with AutoRequired // + // // + /////////////////////////////////////////////////////// + { + // Switch to a child context + AutoCreateContext ctxt; + CurrentContextPusher pshr(ctxt); + + // A single instance of any type can be injected into a context + // This is done using AutoRequied + AutoRequired foo; // We now have a std::shared_ptr + + // AutoRequired will return an instance of the requested type from + // the current context. If that type doesn't exist in the current + // context, it will create an instace + foo->x = 5; + + // If we try to AutoRequire a second Foo, we'll get a pointer to + // the same instance + AutoRequired foo2; + check("foo2 is the same instance as foo", foo==foo2); + std::cout << "foo2 value of 'x': " << foo2->x << std::endl; + + } // 'ctxt' and all members a destroyed when the context goes out of scope + + ////////////////////// + // // + // Autowired // + // // + ////////////////////// + { + { + AutoCreateContext ctxt; + CurrentContextPusher pshr(ctxt); + + // Autowired is similar to AutoRequied, except it doesn't create the + // object if it doesn't already exist. It also searches up the tree + // if the object isn't found in the current context + Autowired foo; + check("foo is an empty pointer", !foo); + + // If we inject to type Foo later, foo will automatically point to that instance + AutoRequired(); + check("foo now contains an instance of Foo", foo); + } + + // This delayed satisfaction also works for class members. + { + AutoCreateContext ctxt; + CurrentContextPusher pshr(ctxt); + + // Bar has an Autowrired classmember + AutoRequired bar; + + check("Foo member of bar is satisfied before AutoRequired", bar->foo); + + // If we inject Foo into the context, the Autowired member of Bar will be satisfied + AutoRequired(); + check("Foo member of bar is satisfied after AutoRequired", bar->foo); + } + } +} \ No newline at end of file diff --git a/examples/EventExample.cpp b/examples/EventExample.cpp new file mode 100644 index 000000000..53cb34ce9 --- /dev/null +++ b/examples/EventExample.cpp @@ -0,0 +1,87 @@ +// Copyright (C) 2012-2014 Leap Motion, Inc. All rights reserved. +#include +#include +#include + +///////////////////////////////////////////////////////////////////// +// // +// Autowiring Events // +// // +///////////////////////////////////////////////////////////////////// + +// Autowiring events are just function calls on member functions in a context. +// All context members that implement function pointer you "fire" will be called + +class MyEvent: + public EventReceiver +{ +public: + virtual void myFunction(int) = 0; +}; + +// Will receive MyEvent::myFunction or FooEvent::myFunction events +class FooEvent: + public MyEvent +{ +public: + FooEvent(): + m_secret(0) + {} + + void myFunction(int i) override { + m_secret = i; + } + + int getSecret(){ + return m_secret; + } +private: + int m_secret; +}; + +// Will receive MyEvent::myFunction or BarEvent::myFunction events +class BarEvent: + public MyEvent +{ +public: + BarEvent(): + m_secret(0) + {} + + void myFunction(int i) override { + m_secret = i; + } + + int getSecret(){ + return m_secret; + } +private: + int m_secret; +}; + +int main(){ + AutoCurrentContext ctxt; + + // A context must be initiated before events can be received. Similar to CoreThread + ctxt->Initiate(); + + // This creates an proxy object that can fire MyEvent::* events + AutoFired eventFirer; + + // Inject receiver types into current context + AutoRequired foo; + AutoRequired bar; + std::cout << "Foo should be 0: " << foo->getSecret() << std::endl; + std::cout << "Bar Should be 0: " << bar->getSecret() << std::endl; + + // Fire event, this should set m_secret on both foo and bar + eventFirer(&MyEvent::myFunction)(42); + std::cout << "Foo should be 42: " << foo->getSecret() << std::endl; + std::cout << "Bar should be 42: " << bar->getSecret() << std::endl; + + // You can also manually fire events on a context with `Invoke` + // Since the function pointer is to `BarEvent`, `FooEvent` won't receive the event + //ctxt->Invoke(&BarEvent::myFunction)(77); + //std::cout << "Foo should be 42: " << foo->getSecret() << std::endl; + //std::cout << "Bar should be 77: " << bar->getSecret() << std::endl; +} \ No newline at end of file diff --git a/examples/ThreadExample.cpp b/examples/ThreadExample.cpp new file mode 100644 index 000000000..d79dbf44d --- /dev/null +++ b/examples/ThreadExample.cpp @@ -0,0 +1,65 @@ +// Copyright (C) 2012-2014 Leap Motion, Inc. All rights reserved. +#include +#include +#include +#include +#include +#include + +class MyBasicThread: + public BasicThread +{ +public: + virtual void Run() override { + for (auto x : {0, 1, 2 ,3}) { + std::cout << "MyBasicThread: " << x << "\n"; + } + } +}; + +class MyCoreThread: + public CoreThread +{ +public: + void AddToQueue(int x) { + *this += [x] { + std::cout << "MyCoreThread: " << x << "\n"; + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + }; + } +}; + +int main(){ + + // The 2 main thread classes in Autowiring are the BasicThread and CoreThread. + // Classes that inherit from these types will have thread capabilities + // Both start when their enclosing context is 'initiated'. Threads injected + // after the context is initiated will start immediatly + + AutoRequired myBasic; + + AutoCurrentContext ctxt; + ctxt->Initiate(); // myBasic->Run() starts now in its own thread + + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + + std::cout << "injecting a CoreThread" << std::endl; + + // Types inheriting from CoreThread implement a dispatch queue in their 'run()' + // function. Lambdas can be appended with operator+= + + AutoRequired myCore; + myCore->AddToQueue(42); + myCore->AddToQueue(1337); + + *myCore += []{ + std::cout << "This gets run after '1337'" << std::endl; + }; + + // This should be run before 'myCore' is finished + std::cout << "This thread is faster\n"; + + // This will wait for all outstanding threads to finish before terminating the context + ctxt->SignalShutdown(true); +} + diff --git a/src/autonet/CMakeLists.txt b/src/autonet/CMakeLists.txt index 997a04107..26738d799 100644 --- a/src/autonet/CMakeLists.txt +++ b/src/autonet/CMakeLists.txt @@ -4,6 +4,11 @@ if(NOT Boost_FOUND) return() endif() +option(AUTOWIRING_BUILD_AUTONET "Build Autonet debugging server" ${AUTOWIRING_BUILD_AUTONET_DEFAULT}) +if(NOT AUTOWIRING_BUILD_AUTONET) + return() +endif() + add_googletest(test) include_directories( ${Boost_INCLUDE_DIR}