Skip to content

Commit

Permalink
cons: rework API to utilize C++11
Browse files Browse the repository at this point in the history
In particular, this CL makes four changes:

1. Taking advantage of C++11's templated type aliases, we can replace
the awkward "Cons<T>::List" type with the somewhat more idiomatic
"cons::List<T>" type.

2. To allow cons lists to be usable with C++11 range-based for loops,
add a C++-style iterator wrapper and begin()/end() functions.

3. Use "nullptr" to create a nil list instead of "Cons<Foo>::List()".

4. Rename the "Cons" type to "Cell" so that "Cons" can instead
idiomatically name the function that constructs a new list cell.

BUG=414363

Review URL: https://codereview.chromium.org/652093002

Cr-Commit-Position: refs/heads/master@{#299633}
  • Loading branch information
mdempsky authored and Commit bot committed Oct 15, 2014
1 parent 8ab0f9e commit 49b8bfb
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 35 deletions.
10 changes: 4 additions & 6 deletions sandbox/linux/bpf_dsl/bpf_dsl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -326,10 +326,10 @@ BoolExpr operator||(const BoolExpr& lhs, const BoolExpr& rhs) {
}

Elser If(const BoolExpr& cond, const ResultExpr& then_result) {
return Elser(Cons<Elser::Clause>::List()).ElseIf(cond, then_result);
return Elser(nullptr).ElseIf(cond, then_result);
}

Elser::Elser(Cons<Clause>::List clause_list) : clause_list_(clause_list) {
Elser::Elser(cons::List<Clause> clause_list) : clause_list_(clause_list) {
}

Elser::Elser(const Elser& elser) : clause_list_(elser.clause_list_) {
Expand All @@ -339,8 +339,7 @@ Elser::~Elser() {
}

Elser Elser::ElseIf(const BoolExpr& cond, const ResultExpr& then_result) const {
return Elser(
Cons<Clause>::Make(std::make_pair(cond, then_result), clause_list_));
return Elser(Cons(std::make_pair(cond, then_result), clause_list_));
}

ResultExpr Elser::Else(const ResultExpr& else_result) const {
Expand All @@ -367,8 +366,7 @@ ResultExpr Elser::Else(const ResultExpr& else_result) const {
// and end up with an appropriately chained tree.

ResultExpr expr = else_result;
for (Cons<Clause>::List it = clause_list_; it.get(); it = it->tail()) {
Clause clause = it->head();
for (const Clause& clause : clause_list_) {
expr = ResultExpr(
new const IfThenResultExprImpl(clause.first, clause.second, expr));
}
Expand Down
6 changes: 3 additions & 3 deletions sandbox/linux/bpf_dsl/bpf_dsl.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,9 +232,9 @@ class SANDBOX_EXPORT Elser {
private:
typedef std::pair<BoolExpr, ResultExpr> Clause;

explicit Elser(Cons<Clause>::List clause_list);
explicit Elser(cons::List<Clause> clause_list);

Cons<Clause>::List clause_list_;
cons::List<Clause> clause_list_;

friend Elser If(const BoolExpr&, const ResultExpr&);
template <typename T>
Expand Down Expand Up @@ -338,7 +338,7 @@ BoolExpr Arg<T>::EqualTo(T val) const {

template <typename T>
SANDBOX_EXPORT Caser<T> Switch(const Arg<T>& arg) {
return Caser<T>(arg, Elser(Cons<Elser::Clause>::List()));
return Caser<T>(arg, Elser(nullptr));
}

template <typename T>
Expand Down
130 changes: 111 additions & 19 deletions sandbox/linux/bpf_dsl/cons.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,130 @@
#include "sandbox/sandbox_export.h"

namespace sandbox {
namespace cons {

// Cons provides an immutable linked list abstraction as commonly
// provided in functional programming languages like Lisp or Haskell.
// Namespace cons provides an abstraction for immutable "cons list"
// data structures as commonly provided in functional programming
// languages like Lisp or Haskell.
//
// A cons list is a linked list consisting of "cells", each of which
// have a "head" and a "tail" element. A cell's head element contains
// a user specified value, while the tail element contains a (possibly
// null) pointer to another cell.
//
// An empty list (idiomatically referred to as "nil") can be
// constructed as "cons::List<Foo>()" or simply as "nullptr" if Foo
// can be inferred from context (e.g., calling a function that has a
// "cons::List<Foo>" parameter).
//
// Existing lists (including empty lists) can be extended by
// prepending new values to the front using the "Cons(head, tail)"
// function, which will allocate a new cons cell. Notably, cons lists
// support creating multiple lists that share a common tail sequence.
//
// Lastly, lists support iteration via C++11's range-based for loop
// construct.
//
// Examples:
//
// // basic construction
// const cons::List<char> kNil = nullptr;
// cons::List<char> ba = Cons('b', Cons('a', kNil));
//
// // common tail sequence
// cons::List<char> cba = Cons('c', ba);
// cons::List<char> dba = Cons('d', ba);
//
// // iteration
// for (const char& ch : cba) {
// // iterates 'c', 'b', 'a'
// }
// for (const char& ch : dba) {
// // iterates 'd', 'b', 'a'
// }

// Forward declarations.
template <typename T>
class Cell;
template <typename T>
class ListIterator;

// List represents a (possibly null) pointer to a cons cell.
template <typename T>
using List = scoped_refptr<const Cell<T>>;

// Cons extends a cons list by prepending a new value to the front.
template <typename T>
List<T> Cons(const T& head, const List<T>& tail) {
return List<T>(new const Cell<T>(head, tail));
}

// Cell represents an individual "cons cell" within a cons list.
template <typename T>
class Cons : public base::RefCounted<Cons<T> > {
class Cell : public base::RefCounted<Cell<T>> {
public:
// List provides an abstraction for referencing a list of zero or
// more Cons nodes.
typedef scoped_refptr<const Cons<T> > List;
Cell(const T& head, const List<T>& tail) : head_(head), tail_(tail) {}

// Return this node's head element.
// Head returns this cell's head element.
const T& head() const { return head_; }

// Return this node's tail element.
List tail() const { return tail_; }

// Construct a new List using |head| and |tail|.
static List Make(const T& head, List tail) {
return make_scoped_refptr(new const Cons<T>(head, tail));
}
// Tail returns this cell's tail element.
const List<T>& tail() const { return tail_; }

private:
Cons(const T& head, List tail) : head_(head), tail_(tail) {}
virtual ~Cons() {}
virtual ~Cell() {}

T head_;
List tail_;
List<T> tail_;

friend class base::RefCounted<Cons<T> >;
DISALLOW_COPY_AND_ASSIGN(Cons);
friend class base::RefCounted<Cell<T>>;
DISALLOW_COPY_AND_ASSIGN(Cell);
};

// Begin returns a list iterator pointing to the first element of the
// cons list. It's provided to support range-based for loops.
template <typename T>
ListIterator<T> begin(const List<T>& list) {
return ListIterator<T>(list);
}

// End returns a list iterator pointing to the "past-the-end" element
// of the cons list (i.e., nil). It's provided to support range-based
// for loops.
template <typename T>
ListIterator<T> end(const List<T>& list) {
return ListIterator<T>();
}

// ListIterator provides C++ forward iterator semantics for traversing
// a cons list.
template <typename T>
class ListIterator {
public:
ListIterator() : list_() {}
explicit ListIterator(const List<T>& list) : list_(list) {}

const T& operator*() const { return list_->head(); }

ListIterator& operator++() {
list_ = list_->tail();
return *this;
}

friend bool operator==(const ListIterator& lhs, const ListIterator& rhs) {
return lhs.list_ == rhs.list_;
}

private:
List<T> list_;
};

template <typename T>
bool operator!=(const ListIterator<T>& lhs, const ListIterator<T>& rhs) {
return !(lhs == rhs);
}

} // namespace cons
} // namespace sandbox

#endif // SANDBOX_LINUX_BPF_DSL_CONS_H_
13 changes: 6 additions & 7 deletions sandbox/linux/bpf_dsl/cons_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,20 @@
namespace sandbox {
namespace {

std::string Join(Cons<char>::List char_list) {
std::string Join(cons::List<char> char_list) {
std::string res;
for (Cons<char>::List it = char_list; it.get(); it = it->tail()) {
res.push_back(it->head());
for (const char& ch : char_list) {
res.push_back(ch);
}
return res;
}

TEST(ConsListTest, Basic) {
Cons<char>::List ba =
Cons<char>::Make('b', Cons<char>::Make('a', Cons<char>::List()));
cons::List<char> ba = Cons('b', Cons('a', cons::List<char>()));
EXPECT_EQ("ba", Join(ba));

Cons<char>::List cba = Cons<char>::Make('c', ba);
Cons<char>::List dba = Cons<char>::Make('d', ba);
cons::List<char> cba = Cons('c', ba);
cons::List<char> dba = Cons('d', ba);
EXPECT_EQ("cba", Join(cba));
EXPECT_EQ("dba", Join(dba));
}
Expand Down

0 comments on commit 49b8bfb

Please sign in to comment.