xpressive: Library Design on the Edge

Download Report

Transcript xpressive: Library Design on the Edge

xpressive:
Library Design on the Edge
or, How I Learned to
Stop Worrying and Love
Template MetaProgramming
Copyright 2005 Eric Niebler
“Why program by hand in five days
what you can spend five years of your
life automating?”
-- Terrence Parr, author ANTLR/PCCTS
Copyright 2005 Eric Niebler
Overview



Domain-Specific (Embedded) Languages
xpressive and Dual-Mode DSEL Design
Programming at the Compile-time / Runtime
boundary
Copyright 2005 Eric Niebler
Domain-Specific Languages

Mini-languages, everywhere!
–
–
–


GNU Make
Backus Naur Form
Regular expressions
Syntax, constructs and abstractions for
working efficiently in some narrow domain
Not general purpose!
Copyright 2005 Eric Niebler
Why DSLs?




General purpose languages are low-level,
procedural
DSLs are high-level and declarative
Solution space shares concepts with problem
space
DSL code is easier to write, read, reason
about and maintain.
Copyright 2005 Eric Niebler
Embedded DSLs



A DSL in a library
All the wholesome goodness of a DSL in the
host language of your choice!
C++ is a good host language
–
–
operator overloading
low abstraction penalty
Copyright 2005 Eric Niebler
Expression Templates



(Ab)uses C++ operator overloading to
approximate the syntax of the embedded
language
Must map embedded language syntax and
constructs into C++
Used by:
–
–
Blitz++ : high-performance scientific computing
Spirit : parser generator (EBNF)
Copyright 2005 Eric Niebler
DSEL example: EBNF Calculator
rule<> group, fact, term, expr;
group
fact
term
expr
= '('
::=
'('
>>expr
expr')'
>> ')';
::=
=
integer
integer
| group;
| group
::=
=
fact
fact
>>(('*'
*(('*'
fact)
>> fact)
| ('/'
| fact))*
('/' >> fact));
::=
=
term
term
>>(('+'
*(('+'
term)
>> term)
| ('-'
| term))*
('-' >> term));
Copyright 2005 Eric Niebler
Types of DSELs: Dynamic

Example:
SQLCommand c = "SELECT * from Employees";

Advantages:
–
–

Unconstrained syntax
Statements can be specified at runtime
Disadvantages:
–
–
Syntax errors discovered at runtime
Performance costs of interpretation
Copyright 2005 Eric Niebler
Types of DSELs: Static

Example:
double d = (matrix * vector)(3, 4);

Advantages:
–
–

Syntax errors checked at compile-time
Aggressive inlining, domain-specific codegen
Disadvantages:
–
–
Constrained by rules for legal C++ expressions
Cannot accept new statements at runtime
Copyright 2005 Eric Niebler
Hrrmmm ...
Static
DSELs...
Can’t I have
it both
ways?
Dynamic
DSELs...
Copyright 2005 Eric Niebler
Dual-Mode DSEL Interface


Provide both a static and dynamic interface!
Sounds good but ...
–
–
Can we really get the advantages of both?
Can we share the implementation to avoid code
duplication?
Copyright 2005 Eric Niebler
Regular Expression
Expression Template
"\\w"
"\\w+"
"a\\w"
"a|b"
"(\\w)\\1"
"[^a-z]"
"(?=foo)"
_w
+_w
'a' >> _w
as_xpr('a') | 'b'
(s1= _w) >> s1
~range('a', 'z')
before("foo")
Copyright 2005 Eric Niebler
Get a Date
// Match a date of the form 10-03-2005
#
regex date = regex::compile(
_d >> !_d >> '-'
// match month
/\d\d?-\d\d?-\d\d(?:\d\d)?/
"\\d\\d?-\\d\\d?-\\d\\d(?:\\d\\d)?");
"\\d\\d?-\\d\\d?-\\d\\d(?:\\d\\d)?";
>> _d >> !_d >> '-'
// match day
>> _d >> _d >> !(_d >> _d);
// match year
Copyright 2005 Eric Niebler
Regex aliases, anyone?
regex date = /* ... */;
// A line in a log file is a date followed by a
// space, and everything up to the newline.
regex log = date >> ' ' >> +~set['\n'];
Copyright 2005 Eric Niebler
Semantic Constraints
// Only match valid dates
regex date =
(_d >> !_d)[if_is_month()] >> '-'
>> (_d >> !_d)[if_is_day()]
>> '-'
>> (_d >> _d >> !(_d >> _d))[if_is_year()];
Copyright 2005 Eric Niebler
Two great tastes ...
... that taste great together
// A line in a log file is a date followed by a
// space, and everything up to the newline.
regex date = regex::compile(get_date_pattern());
regex log = date >> ' ' >> +~set['\n'];
Copyright 2005 Eric Niebler
“Some people, when confronted with
a problem, think, ‘I know, I’ll use
regular expressions.’ Now they have
two problems.”
--Jamie Zawinski, in comp.lang.emacs
Copyright 2005 Eric Niebler
Recursive regexen!
regex parens;
parens
= '('
>>
*(
keep( +~(set='(',')') )
|
by_ref(parens)
)
>>
')'
//
//
//
//
//
//
//
//
//
//
//
A balanced set of parens ...
is an opening paren ...
followed by ...
zero or more ...
of a bunch of things that are
not parens ...
or ...
a balanced set of parens
(ooh, recursion!) ...
followed by ...
a closing paren
;
Copyright 2005 Eric Niebler
A Regex Calculator?!
regex group, fact, term, expr;
group
fact
term
expr
=
=
=
=
'(' >> by_ref(expr) >> ')';
+_d | group;
fact >> *(('*' >> fact) | ('/' >> fact));
term >> *(('+' >> term) | ('-' >> term));
Copyright 2005 Eric Niebler
C++ library design at the edge
Wife:
Husband:
Announcer:
It's a floor wax!
No, it's a dessert topping!
Stop! You're both right. It's a
floor wax and a dessert
topping!
-- Saturday Night Live
Copyright 2005 Eric Niebler
STL, MPL and Fusion, Oh My!
// Just the data, ma’am
std::list<int> integers;
// Just the types, ma’am
typedef
mpl::list<int, double, std::string>
types;
// Types and data, please!
fusion::tuple<int, double, std::string> data
= fusion::make_tuple(1, 3.14, "hello");
Copyright 2005 Eric Niebler
The Fusion Library


Heterogeneous data structures
STL-influenced
–


containers, iterators, algorithms
MPL-compatible
by Joel de Guzman, part of Boost.Spirit
–
http://spirit.sourceforge.net
Copyright 2005 Eric Niebler
A Simple Fusion-esque List
struct nil = {};
template<class Car, class Cdr = nil>
struct cons {
Car car; Cdr cdr;
cons(Car const & a, Cdr const & d = Cdr())
: car(a), cdr(d) {}
};
inline cons<Car,Cdr>
make_cons(Car const & a, Cdr const & d)
{ return cons(a, d); }
Copyright 2005 Eric Niebler
Simple Fusion-esque List, cont.
cons<int,cons<double,cons<std::string> > > data =
make_cons(1,
make_cons(3.14,
make_cons(std::string("hello"))));
Copyright 2005 Eric Niebler
Fusion-esque algorithms
template<typename F>
void for_each(nil, F) {}
template<class Car, class Cdr, class F>
void for_each(cons<Car, Cdr> const & l, F f)
{
f(l.car);
for_each(l.cdr, f);
}
Copyright 2005 Eric Niebler
Programming challenge!!!
Write the type of a std::pair<First, Second>
where Second is a pointer to the whole
std::pair ...
std::pair<int, std::pair<int,
??? *>
???*> *>
std::pair<int, ???*> *> *>
std::pair<int,
std::pair<int,
std::pair<int,
std::pair<int,
Copyright 2005 Eric Niebler
std::pair<int,
Dual-Mode DSEL Design Strategy




recursive algorithms
one modular core
two binding policies: static and dynamic
acyclic data structures
Copyright 2005 Eric Niebler
xpressive Matchers
// Match any single character
struct any_matcher
{
template< class Iter, class Next >
bool match(Iter i1, Iter i2, Next const &n) const
{
if ( i1 == i2 ) { return false; }
return n.match( ++i1, i2 );
}
};
Copyright 2005 Eric Niebler
xpressive static Scaffold
template< class Matcher, class Next >
struct static_xpression {
Matcher matcher;
Next
next;
template< class Iter >
bool match( Iter i1, Iter i2 ) const {
return matcher.match( i1, i2, next );
}
};
Copyright 2005 Eric Niebler
xpressive dynamic Scaffold
template< class Iter >
struct matchable {
virtual bool match( Iter, Iter ) const = 0;
};
Copyright 2005 Eric Niebler
xpr: dynamic Scaffold, cont.
template< class Matcher, class Iter >
struct dynamic_xpression : matchable< Iter > {
Matcher matcher;
matchable< Iter > * pnext;
bool match( Iter i1, Iter i2 ) const {
return matcher.match( i1, i2, *pnext );
}
};
Copyright 2005 Eric Niebler
Separation of Concerns




Matchers implement core functionality
Scaffolds implement binding policy (static or
dynamic)
Matchers are binding-neutral, reusable
Best of both worlds
–
–
perf of static binding
flexibility of dynamic dispatch
Copyright 2005 Eric Niebler
References

xpressive:
–

http://boost-sandbox.sf.net/libs/xpressive
Spirit and Fusion
–
–
by Joel de Guzman
http://spirit.sf.net
Copyright 2005 Eric Niebler
Questions?
Copyright 2005 Eric Niebler