Skip to content

Latest commit

 

History

History
486 lines (440 loc) · 16.7 KB

README.md

File metadata and controls

486 lines (440 loc) · 16.7 KB

Bricks

HTTP server and client, JSON and binary serialization, visualization and other core pieces to be reused across Current projects.

Image credit: Bender from Futurama. Found via Bing image search.

The following documentation has been auto-generated from source code by means of a gen-docu.sh script. Do not edit this file.

Release Notes

  • 3/4/2015 : 1.0, Initial public release.

    Documentation, tar.gz, zip.

    HTTP server and client API-s, Cereal for binary and JSON serialization, gnuplot and plotutils ports, file system and string manipulation methods, a command line flags parsing tool and a header-only port of GoogleTest.

    Cross-platform, tested on Linux, Mac and Windows. 100% unit tested.

    Header-only C++11 with no external library dependencies.

Documentation

Visualization Library

Bricks has C++ bindings for gnuplot, #include "Bricks/graph/gnuplot.h" to use it.

External gnuplot binary is invoked. The requirement is that it should be installed in the system and accessible in the $PATH.

// Where visualization meets love.
using namespace current::gnuplot;
const size_t image_dim = 800;
const std::string result = GNUPlot()
  .Title("Imagine all the people ...")
  .NoKey()
  .Grid("back")
  .XLabel("... living life in peace")
  .YLabel("John Lennon, \"Imagine\"")
  .Plot(WithMeta([](Plotter p) {
    const size_t N = 1000;
    for (size_t i = 0; i < N; ++i) {
      const double t = M_PI * 2 * i / (N - 1);
      p(16 * pow(sin(t), 3),
        -(13 * cos(t) + 5 * cos(t * 2) - 2 * cos(t * 3) - cos(t * 4)));
    }
  }).LineWidth(5).Color("rgb '#FF0080'"))
  .ImageSize(image_dim)
  .OutputFormat("svg");  // Although the one below is actually a "png".

// Where visualization meets science.
using namespace current::gnuplot;
const size_t image_dim = 800;
const std::string result = GNUPlot()
  .Title("Graph 'title' with various \"quotes\"")
  .KeyTitle("'Legend', also known as the \"key\"")
  .XRange(-5, +5)
  .YRange(-2, +2)
  .Grid("back")
  .Plot([](Plotter& p) {
    for (int i = -50; i <= +50; ++i) {
      p(0.1 * i, ::sin(0.1 * i));
    }
  })
  .Plot(WithMeta([](Plotter& p) {
                   for (int i = -50; i <= +50; ++i) {
                     p(0.1 * i, ::cos(0.1 * i));
                   }
                 })
            .AsPoints()
            .Color("rgb 'blue'")
            .Name("\"cos(x)\", '.AsPoints().Color(\"rgb 'blue'\")'"))
  .ImageSize(image_dim)
  .OutputFormat("svg");  // Although the one below is actually a "png".

#include "../../strings/printf.h"

// Show labels on the plane.
using namespace current::gnuplot;
const size_t image_dim = 800;
const std::string result = GNUPlot()
  .Title("Labeled Points")
  .NoKey()
  .NoTics()
  .NoBorder()
  .Grid("back")
  .XRange(-1.5, +1.5)
  .YRange(-1.5, +1.5)
  .Plot(WithMeta([](Plotter& p) {
    const int N = 7;
    for (int i = 0; i < N; ++i) {
      const double phi = M_PI * 2 * i / N;
      p(cos(phi), sin(phi), current::strings::Printf("P%d", i));
    }
  }).AsLabels())
  .ImageSize(image_dim)
  .OutputFormat("svg");  // Although the one below is actually a "png".

Functional Template Library

Bricks makes extensive use of C++11 variadic templates. A few generic methods are exposed as current::metaprogramming.

// Map.
template<typename T> struct add_100 { enum { x = T::x + 100 }; };

struct A { enum { x = 1 }; };
struct B { enum { x = 2 }; };
struct C { enum { x = 3 }; };

typedef TypeList<A, B, C> TYPELIST_BEFORE;
static_assert(TypeListSize<TYPELIST_BEFORE>::value == 3, "");
static_assert(TypeListElement<0, TYPELIST_BEFORE>::x == 1, "");
static_assert(TypeListElement<1, TYPELIST_BEFORE>::x == 2, "");
static_assert(TypeListElement<2, TYPELIST_BEFORE>::x == 3, "");

typedef current::metaprogramming::map<add_100, TYPELIST_BEFORE> TYPELIST_AFTER;
static_assert(TypeListSize<TYPELIST_AFTER>::value == 3, "");
static_assert(TypeListElement<0, TYPELIST_AFTER>::x == 101, "");
static_assert(TypeListElement<1, TYPELIST_AFTER>::x == 102, "");
static_assert(TypeListElement<2, TYPELIST_AFTER>::x == 103, "");

// Filter.
template <typename T> struct y_is_even { enum { filter = ((T::y % 2) == 0) }; };

struct A { enum { y = 10 }; };
struct B { enum { y = 15 }; };
struct C { enum { y = 20 }; };
typedef TypeList<A, B, C> TYPELIST_BEFORE;
static_assert(TypeListSize<TYPELIST_BEFORE>::value == 3, "");
static_assert(TypeListElement<0, TYPELIST_BEFORE>::y == 10, "");
static_assert(TypeListElement<1, TYPELIST_BEFORE>::y == 15, "");
static_assert(TypeListElement<2, TYPELIST_BEFORE>::y == 20, "");

typedef current::metaprogramming::filter<y_is_even, TYPELIST_BEFORE> TYPELIST_AFTER;
static_assert(TypeListSize<TYPELIST_AFTER>::value == 2, "");
static_assert(TypeListElement<0, TYPELIST_AFTER>::y == 10, "");
static_assert(TypeListElement<1, TYPELIST_AFTER>::y == 20, "");
  
// Reduce.
template<typename A, typename B> struct concatenate_s {
  static std::string s() { return "(" + A::s() + "+" + B::s() + ")"; }
};
    
struct A { static std::string s() { return "A"; } };
struct B { static std::string s() { return "B"; } };
struct C { static std::string s() { return "C"; } };
EXPECT_EQ("(A+(B+C))", (current::metaprogramming::reduce<concatenate_s, TypeList<A, B, C>>::s()));
  
// Combine.
struct NEG {
  // A simple way to differentiate logic by struct/class type
  // is to use a helper local type as the 1st param in the signature.
  struct TYPE {};
  // `operator()` on an instance of a `combine`-d type
  // calls `operator()` of the type from the type list that matches
  // the types of parameters passed in.
  // If none or more than one of the input types have the matching signature,
  // attempting such a call will result in compilation error.
  int operator()(TYPE, int a) { return -a; }
  // `DispatchToAll()` on an instance of `combine`-d type
  // calls `DispatchToAll()` from all combined classes,
  // in the order they have been listed in the type list.
  template<typename T> void DispatchToAll(T& x) { x.result += "NEG\n"; }
};

struct ADD {
  struct TYPE {};
  int operator()(TYPE, int a, int b) { return a + b; }
  // Prove that the method is instantiated at compile time.
  template <typename T>
  int operator()(TYPE, int a, int b, T c) {
    return a + b + AsInt(c);
  }
  template<typename T> void DispatchToAll(T& x) { x.result += "ADD\n"; }
};

// Since "has-a" is used instead of "is-a",
// mutual inheritance of underlying types
// is not a problem at all.
// Confirm this by making `MUL` inherit from `ADD`.
struct MUL : ADD {
  struct TYPE {};
  int operator()(TYPE, int a, int b) { return a * b; }
  int operator()(TYPE, int a, int b, int c) { return a * b * c; }
  template<typename T> void DispatchToAll(T& x) { x.result += "MUL\n"; }
};
  
// User-friendly method names, internally dispatching calls via `operator()`.
// A good way to make sure new names appear in one place only, since
// using `using`-s would require writing them down at least twice each.
struct UserFriendlyArithmetics :
    current::metaprogramming::combine<TypeList<NEG, ADD, MUL>> {
  int Neg(int x) {
    return operator()(NEG::TYPE(), x);
  }
  template <typename... T>
  int Add(T... xs) {
    return operator()(ADD::TYPE(), xs...);
  }
  template <typename... T>
  int Mul(T... xs) {
    return operator()(MUL::TYPE(), xs...);
  }
  // The implementation for `Div()` is not provided,
  // yet the code will compile until it's attempted to be used.
  // (Unit tests and code coverage measurement FTW!)
  template <typename... T>
  int Div(T... xs) {
    struct TypeForWhichThereIsNoImplemenation {};
    return operator()(TypeForWhichThereIsNoImplemenation(),
                      xs...);
  }
};
EXPECT_EQ(1, NEG()(NEG::TYPE(), -1));
EXPECT_EQ(3, ADD()(ADD::TYPE(), 1, 2));
EXPECT_EQ(6, ADD()(ADD::TYPE(), 1, 2, "3"));
EXPECT_EQ(20, MUL()(MUL::TYPE(), 4, 5));
EXPECT_EQ(120, MUL()(MUL::TYPE(), 4, 5, 6));

// As a sanity check, since `MUL` inherits from `ADD`,
// the following construct will work just fine.
EXPECT_EQ(15, MUL().ADD::operator()(ADD::TYPE(), 7, 8));

typedef current::metaprogramming::combine<TypeList<NEG, ADD, MUL>> Arithmetics;
EXPECT_EQ(-1, Arithmetics()(NEG::TYPE(), 1));
EXPECT_EQ(5, Arithmetics()(ADD::TYPE(), 2, 3));
EXPECT_EQ(9, Arithmetics()(ADD::TYPE(), 2, 3, "4"));
EXPECT_EQ(30, Arithmetics()(MUL::TYPE(), 5, 6));
EXPECT_EQ(210, Arithmetics()(MUL::TYPE(), 5, 6, 7));

// Using the dispatched methods.
EXPECT_EQ(42, UserFriendlyArithmetics().Neg(-42));
EXPECT_EQ(21, UserFriendlyArithmetics().Add(10, 11));
EXPECT_EQ(33, UserFriendlyArithmetics().Add(10, 11, "12"));
EXPECT_EQ(420, UserFriendlyArithmetics().Mul(20, 21));
EXPECT_EQ(9240, UserFriendlyArithmetics().Mul(20, 21, 22));
  
// The following call will fail to compile,
// with a nice error message explaining
// that none of the `NEG`, `ADD` and `MUL`
// have division operation defined.
//
// UserFriendlyArithmetics().Div(100, 5);

struct Magic {
  std::string result;
};
Magic magic;
UserFriendlyArithmetics().DispatchToAll(magic);
EXPECT_EQ("NEG\nADD\nMUL\n", magic.result);

// RTTI.
struct BASE {
  // Need a virtual base.
  virtual ~BASE() = default;
};
struct A : virtual BASE {
  int a = 100;
  void foo(std::ostringstream& os) {
    ++a;
    os << "mutable a=" << a;
  }
  void foo(std::ostringstream& os) const { os << "const a=" << a; }
};
// Inherit from `A` as well, just to show that we can.
struct B : virtual A, virtual BASE {
  int b = 200;
  void bar(std::ostringstream& os) {
    ++b;
    os << "mutable b=" << b;
  }
  void bar(std::ostringstream& os) const { os << "const b=" << b; }
};
// Even more "multiple" inheritance.
struct C : virtual A, virtual B, virtual BASE {
  int c = 300;
  void baz(std::ostringstream& os) {
    ++c;
    os << "mutable c=" << c;
  }
  void baz(std::ostringstream& os) const { os << "const c=" << c; }
};
A a;
B b;
C c;

const BASE& const_pa = a;
const BASE& const_pb = b;
const BASE& const_pc = c;

BASE& mutable_pa = a;
BASE& mutable_pb = b;

struct call_foo_bar {
  void operator()(const A& a) { a.foo(os); }
  void operator()(A& a) {
    // Mutable version.
    a.foo(os);
  }
  void operator()(const B& b) { b.bar(os); }
  void operator()(B& b) {
    // Mutable version.
    b.bar(os);
  }
  std::ostringstream os;
};
{
  call_foo_bar foo_bar;
  RTTIDynamicCall<TypeList<A, B>>(const_pa, foo_bar);
  EXPECT_EQ("const a=100", foo_bar.os.str());
}
{
  call_foo_bar foo_bar;
  RTTIDynamicCall<TypeList<A, B>>(const_pb, foo_bar);
  EXPECT_EQ("const b=200", foo_bar.os.str());
}
{
  call_foo_bar foo_bar;
  RTTIDynamicCall<TypeList<A, B>>(mutable_pa, foo_bar);
  EXPECT_EQ("mutable a=101", foo_bar.os.str());
}
{
  call_foo_bar foo_bar;
  RTTIDynamicCall<TypeList<A, B>>(mutable_pb, foo_bar);
  EXPECT_EQ("mutable b=201", foo_bar.os.str());
}
{
  call_foo_bar foo_bar;
  RTTIDynamicCall<TypeList<A, B>>(const_pa, foo_bar);
  EXPECT_EQ("const a=101", foo_bar.os.str());
}
{
  call_foo_bar foo_bar;
  RTTIDynamicCall<TypeList<A, B>>(const_pb, foo_bar);
  EXPECT_EQ("const b=201", foo_bar.os.str());
}
struct call_bar_baz {
  void operator()(const B& b) { b.bar(os); }
  void operator()(const C& c) { c.baz(os); }
  std::ostringstream os;
};
{
  call_bar_baz bar_baz;
  RTTIDynamicCall<TypeList<B, C>>(const_pb, bar_baz);
  EXPECT_EQ("const b=201", bar_baz.os.str());
}
{
  call_bar_baz bar_baz;
  RTTIDynamicCall<TypeList<B, C>>(const_pc, bar_baz);
  EXPECT_EQ("const c=300", bar_baz.os.str());
}
struct call_foo_baz {
  void operator()(const A& a) { a.foo(os); }
  void operator()(A& a) {
    // Mutable version.
    a.foo(os);
  }
  void operator()(const C& c) { c.baz(os); }
  void operator()(C& c) {
    // Mutable version.
    c.baz(os);
  }
  void operator()(const A& a, int x, const std::string& y) {
    a.foo(os);
    os << ", [" << x << "]['" << y << "']";
  }
  void operator()(A& a, int x, const std::string& y) {
    // Mutable version.
    a.foo(os);
    os << ", [" << x << "]['" << y << "']";
  }
  void operator()(const C& c, int x, const std::string& y) {
    c.baz(os);
    os << ", [" << x << "]['" << y << "']";
  }
  void operator()(C& c, int x, const std::string& y) {
    // Mutable version.
    c.baz(os);
    os << ", [" << x << "]['" << y << "']";
  }
  std::ostringstream os;
};
std::unique_ptr<BASE> unique_a(new A());
std::unique_ptr<BASE> unique_c(new C());
BASE* ptr_a = &*unique_a;
BASE* ptr_c = &*unique_c;
{
  call_foo_baz foo_baz;
  RTTIDynamicCall<TypeList<A, C>>(*ptr_a, foo_baz);
  EXPECT_EQ("mutable a=101", foo_baz.os.str());
}
{
  call_foo_baz foo_baz;
  RTTIDynamicCall<TypeList<A, C>>(*ptr_c, foo_baz);
  EXPECT_EQ("mutable c=301", foo_baz.os.str());
}
{
  call_foo_baz foo_baz;
  RTTIDynamicCall<TypeList<A, C>>(*static_cast<const std::unique_ptr<BASE>&>(unique_a), foo_baz);
  EXPECT_EQ("mutable a=102", foo_baz.os.str());
}
{
  call_foo_baz foo_baz;
  RTTIDynamicCall<TypeList<A, C>>(*static_cast<const std::unique_ptr<BASE>&>(unique_c), foo_baz);
  EXPECT_EQ("mutable c=302", foo_baz.os.str());
}
{
  call_foo_baz foo_baz;
  RTTIDynamicCall<TypeList<A, C>>(*ptr_a, foo_baz);
  EXPECT_EQ("mutable a=103", foo_baz.os.str());
}
{
  call_foo_baz foo_baz;
  RTTIDynamicCall<TypeList<A, C>>(*ptr_c, foo_baz);
  EXPECT_EQ("mutable c=303", foo_baz.os.str());
}
{
  call_foo_baz foo_baz;
  RTTIDynamicCall<TypeList<A, C>>(*ptr_a, foo_baz, 1, std::string("one"));
  EXPECT_EQ("mutable a=104, [1]['one']", foo_baz.os.str());
}
{
  call_foo_baz foo_baz;
  RTTIDynamicCall<TypeList<A, C>>(*ptr_c, foo_baz, 2, std::string("two"));
  EXPECT_EQ("mutable c=304, [2]['two']", foo_baz.os.str());
}
struct call_foo_bar_by_rvalue_reference {
  void operator()(A&& a) { a.foo(os); }
  void operator()(B&& b) { b.bar(os); }
  std::ostringstream os;
};
{
  call_foo_bar_by_rvalue_reference foo_bar;
  A a;
  RTTIDynamicCall<TypeList<A, B>>(std::move(a), foo_bar);
  EXPECT_EQ("mutable a=101", foo_bar.os.str());
}
{
  call_foo_bar foo_bar_1;
  call_foo_bar_by_rvalue_reference foo_bar_2;
  B b;
  RTTIDynamicCall<TypeList<A, B>>(b, foo_bar_1);
  RTTIDynamicCall<TypeList<A, B>>(std::move(b), foo_bar_2);
  EXPECT_EQ("mutable b=201", foo_bar_1.os.str());
  EXPECT_EQ("mutable b=202", foo_bar_2.os.str());
}

Command Line Parsing: dflags

Bricks has dflags: a C++ library to parse command-line flags.

DEFINE_int32(answer, 42, "Human-readable flag description.");
DEFINE_string(question, "six by nine", "Another human-readable flag description.");

void example() {
  std::cout << FLAGS_question.length() << ' ' << FLAGS_answer * FLAGS_answer << std::endl;
}

int main(int argc, char** argv) {
  ParseDFlags(&argc, &argv);
  // `google::ParseCommandLineFlags(&argc, &argv);`
  // is supported as well for compatibility reasons.
  example();
}

Supported types are string as std::string, int32, uint32, int64, uint64, float, double and bool. Booleans accept 0/1 and lowercase or capitalized true/false/yes/no.

Flags can be passed in as -flag=value, --flag=value, -flag value or --flag value parameters.

Undefined flag triggers an error message dumped into stderr followed by exit(-1). Same happens if ParseDFlags() was called more than once.

Non-flag parameters are kept; ParseDFlags() replaces argc/argv with the new, updated values, eliminating the ones holding the parsed flags. In other words ./main foo --flag_bar=bar baz results in new argc == 2, new argv == { argv[0], "foo", "baz" }.

Passing --help will cause ParseDFlags() to print all registered flags with their descriptions and exit(0).

dflags is a simplified header-only version of Google's gflags. It requires no linker dependencies and largely is backwards-compatible.

Extras

Bricks contains several other useful bits, including cross-platform file system wrapper, string manipulation functions, in-memory message queue and system clock utilities.