Skip to content

Instantly share code, notes, and snippets.

@Seikilos
Last active August 29, 2015 14:02
Show Gist options
  • Save Seikilos/3cc68b8aa85e4edad277 to your computer and use it in GitHub Desktop.
Save Seikilos/3cc68b8aa85e4edad277 to your computer and use it in GitHub Desktop.
Pipeline class utilizing C++11 Features, handles Visual Studio 2012 incompleteness of C++11 standard. Now with conditional items utilizing predicates
#pragma once
#include <string>
#include <functional>
#include <vector>
#include <utility>
/**
* Pipeline class.
*
* Note: This class is not copy assignable or copy constructible but movable! It is sealed per design.
* Use a wrapped TItem in order to execute specific operations for a given object prior/after calling the inner element
*/
template<typename TDataObject, typename TItem = std::function<void(TDataObject)>>
class Pipeline final
{
public:
typedef typename TDataObject DataType;
typedef typename std::function<bool(DataType)> Predicate;
bool static ALWAYS_TRUE(DataType)
{
return true;
}
bool static ALWAYS_FALSE(DataType)
{
return false;
}
Pipeline(const std::string name):
m_sName(name)
{
}
/**
* Move assignment operator
*/
Pipeline& operator=(Pipeline&& other)
{
if (this != &other)
{
std::swap(m_sName,other.m_sName);
other.m_sName.clear();
std::swap(m_vPipeline, other.m_vPipeline);
other.m_vPipeline.clear();
}
return *this;
}
/**
* Move constructor
*/
Pipeline (Pipeline&& other)
{
std::swap(m_sName,other.m_sName);
other.m_sName.clear();
std::swap(m_vPipeline, other.m_vPipeline);
other.m_vPipeline.clear();
}
/**
* Adds a type of TItem into pipeline
*/
void Add(TItem item, Predicate filter = &Pipeline::ALWAYS_TRUE)
{
m_vPipeline.push_back(std::make_tuple(item,filter));
}
/**
* Helper for easier class method addition
*/
template<typename TMethodAddress, typename TInstance>
void AddInstance(TMethodAddress address, TInstance instance, Predicate filter = &Pipeline::ALWAYS_TRUE)
{
Add(std::bind(address, instance, std::placeholders::_1), filter);
}
/**
* Helper returns a TItem to be added via Add(TItem) or with the variadic template argument version of Add(...)
*/
template<typename TMethodAddress, typename TInstance>
inline TItem Get(TMethodAddress address, TInstance instance)
{
return (std::bind(address, instance, std::placeholders::_1));
}
#if _MSC_VER >= 1800
// Only with VS 2013 and higher, allows chaining of several arguments
template <typename ...Tail>
void Add(TItem head, Tail... tail)
{
Add(head);
Add(std::forward<Tail>(tail)...);
}
#endif
void Execute(TDataObject argument)
{
for(auto object : m_vPipeline)
{
if(std::get<1>(object)(argument) == true)
{
std::get<0>(object)(argument);
}
}
}
const std::string & GetName() const { return m_sName; }
protected:
std::string m_sName;
std::vector<std::tuple<TItem, Predicate>> m_vPipeline;
private:
Pipeline(const Pipeline&); // prevent copy constructor to be used
Pipeline& operator=(const Pipeline&); // prevent copy assignment to be used
};
// Simple example + moving of pipeline
{
Pipeline<int> p("Sample path");
p.Add([](int i){std::cout << i+1 << std::endl;});
p.Add([](int i){std::cout << i+42 << std::endl;});
auto f = std::move(p); // moveable class
f.Execute(4);
}
// How to pass a pipeline around without moving
{
std::unique_ptr<Pipeline<int>> p2(new Pipeline<int>("second"));
auto & p3 = p2;
}
// Using Wrapper class without altering the Add() signature
{
Pipeline<int, Wrapper<FuncType>> p("Wrapped pipeline"); // <-- utilizing Wrapper
p.Add([](int i){std::cout << i+1 << std::endl;});
p.Add([](int i){std::cout << i+42 << std::endl;});
p.Execute(4);
}
// Binding methods of class and utilizing C++11 variadic templates (VS 2013)
class Dummy
{
public:
void method(int i)
{
}
};
Dummy d;
Pipeline<int> p("Variading path");
p.Add(
[](int i){std::cout << i + 1 << std::endl; }, // Lambda
std::bind(&Dummy::method, &d, std::placeholders::_1), // Standard bind
p.Get(&Dummy::method, &d) // Helper to remove verbose bindg call and placeholder
);
// Another helper for directly adding a bind instance (used when compiler does not support variadic template arguments)
p.Add(&Dummy::method, &d);
p.Execute(4);
// Predicate
Pipeline<int &> p("Filter");
p.Add([](int & i) {++i;}, Pipeline<int &>::ALWAYS_FALSE);
p.Add([](int & i) {i+=2;}, [](int & v) { return v > 10;});
p.Add([](int & i) {i+=2;});
auto number = 42;
p.Execute(number);
assert(number == 46);
#pragma once
#include <functional>
#include <vector>
typedef std::function<void(int)> FuncType;
template<class T>
class Wrapper
{
public:
template<
typename U,
typename std::enable_if< std::is_constructible<T, U>::value, int >::type = 0
>
//use template<class U> for VS2012
Wrapper(U t)
: _t(std::move(t))
{}
void operator()(int i) const
{
// Do wrapped code here e.g. benchmark, logging of _t.target_type().name()
_t(i);
}
private:
T _t;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment