Geordi is an IRC bot program that compiles and (optionally) runs C++ code snippets. It is intended to be used as a demonstration tool when teaching or discussing C++ on IRC.
A geordi bot by the name "geordi" is present on Freenode in the channels ##iso-c++, ##c++, and #geordi. It is referred to as geordi prime to avoid confusion with the software. This manual was mostly written with geordi prime in mind. Other geordi instances may have altered usage syntaxes, preludes, compiler flags, rules, et cetera.
Geordi is public domain, free software.
Geordi has 3 main request syntaxes, demonstrated by example below.
To print things, stream things to geordi as if he were std::cout:
<Eelis> geordi << 3 * 2 << " foo " << (9 > 4)
<geordi> 6 foo true
To accommodate the use of nick completion, "geordi" may be followed by a colon or comma.
The code may be followed by a semicolon followed by more code, which is then placed before the definition of main (where the printing statement is put):
<Eelis> geordi << f(3); int f(int x) { return x; }
<geordi> 3
To execute a block of statements, say for example:
<Eelis> geordi { string s = "foo"; cout << s; }
<geordi> foo
Here, too, "geordi" may be followed by a colon or comma.
The closing brace may be followed by more code, which is then placed before the definition of main (where the statement block is put):
<Eelis> geordi { cout << f(3); } int f(int x) { return x; }
<geordi> 3
To execute programs, say for example:
<Eelis> geordi: void f(int x) { cout << x; } int main() { f(3); }
<geordi> 3
Here, a colon or comma after "geordi" is required if the next non-space character is a letter, an asterisk, a slash, or a single quote.
Whereas in ordinary C++ backslashes (outside of character/string literals) splice physical source lines to form a logical source line, in geordi snippets this behavior is inverted; backslashes split the physical source line into logical source lines.
Two consecutive newlines (i.e. \\) split the snippet into multiple translation units.
In all three syntaxes described in the previous section, flags may be passed before any code. For the first two syntaxes, that means before << and {, respectively.
Only compile; do not assemble, link, or run.
<Eelis> geordi -c void f (string & x) { reverse(x.begin(), x.end()); }
<geordi> Success
This better expresses your intent in demonstrations and produces a faster and more appropriate response from geordi. With -c, a main function is optional.
Suppress warnings.
Terse mode. Adds #include "terse.hpp", which defines shorter names for things.
<Eelis> geordi -tc use ns boost; tmp <tpn T> cls C { expl C (C co &); pvt: stc dub d; pub: void op() (); };
<geordi> Compilation successful
Omits the otherwise implicitly added "using namespace std;".
Shows URL for this manual.
Shows the GCC version.
Shows the flags passed to GCC.
Shows the type described in C++ syntax.
<Eelis> geordi --make-type pointer to function returning a reference to an array of three pointers to functions taking integers and returning doubles
<geordi> double(*(&(*)())[3])(int)
Shows the given expression with added parentheses to illustrate precedence and associativity of operators.
<Eelis> geordi --precedence a + b - c ? d : e+++f
<geordi> ((a + b) - c) ? d : ((e++) + f)
Adds the previous snippet's code, except for its main function (if any).
<Eelis> geordi << f(); int f() { return 3; }
<geordi> 3
<Eelis> geordi -r << f() + g(); double g() { return 1.2; }
<geordi> 4.2
<Eelis> geordi, show
<geordi> << f() + g(); int f() { return 3; } double g() { return 1.2; }
Geordi provides a set of editing commands that modify and then retry the last snippet.
command = (insert | append | prepend | erase | replace | move | swap | use | make)*
insert = ("insert" | "add") (... positions* | wrapping ("around" substrs*)*)
append = "append" ... [positions*]
prepend = "prepend" (options | ...) [positions*]
erase = ("erase" | "remove" | "delete" | "cut" | "omit" | "kill") (substrs | options)*
replace = "replace" (substrs* ("with" | "by") ... | options ("with" | "by") options)*
move = "move" (substrs "to" position)*
swap = "swap" (substr "and" substr)*
use = "use" (options | ... [relative])*
make = "make" declarator-id* type-description
wrapping = ... "and" ... | "parentheses" | "parens" | "braces" | "curlies"
| ("square" | "angle" | "curly" | "round") "brackets"
| ("single" | "double") "quotes"
position = limit | befaft (substr [relative])
positions = in-clause | befaft substrs
substr = "everything" | [ordinal] (named-entity | ...)
substrs = (("everything" | rankeds (named-entity | ...)) [relative])*
befaft = "before" | "after"
limit = "begin" | "front" | "end" | "back"
ordinal = "first" | ("second" | "third" | etc) ["last"] | "last"
rankeds = "all" [("except" | "but") ordinal*] | ["each" | "every" | "any" | ordinal*]
in-clause = "in" ([ordinal] (named-entity | declarator-id) [relative])*
named-entity = ("declaration" | "body") "of" declarator-id
relative = between | befaft [ranked] substr | in-clause
between = "between" (bound "and" relative-bound | ordinal "and" ordinal ...)
range = ([["everything"] "from"] bound | "everything") ("till" | "until") relative-bound
bound = limit | [befaft] substr
relative-bound = limit | [befaft] substr [relative]
Ellipsis denote simple verbatim strings without escaping, quoting, globbing, etc. The notation x* stands for x ["and" x*] .
<Johnny> geordi: string s = "foo"; cin << s; }
<geordi> error: expected constructor, destructor, or type conversion before '<<' token
<Eelis> geordi: prepend { and replace cin with cout
<geordi> foo
<Johnny> Ah, I see!
Fixing longer snippets this way is easier and more to the point than copy&paste-ing.
The last snippet can be edited and re-edited indefinitely. Use the "show" command to show the snippet in its current form:
<Johnny> So, what does the snippet look like with those changes?
<Eelis> geordi: show
<geordi> {string s = "foo"; cout << s;}
<Johnny> Ah, I see!
More examples:
geordi, wrap parentheses around everything and erase second semicolon
geordi, insert << flush before second last << and move int i; to before int j = 3 * i;
geordi, move everything after int main() to front and erase int main()
For each given string, the "use" edit command searches the previous snippet for the substring that most resembles the given string, and replaces the former with the latter. This can be used to succinctly fix typos or make small adjustments:
<Johnny> geordi: -c class X { void f() }; void main() { statc X x(); x->f(); }
<geordi> error: expected ';' before '}' token
<Eelis> geordi: use void f(); }
<geordi> error: '::main' must return 'int'
<Eelis> geordi: use int main
<geordi> error: 'statc' was not declared in this scope
<Eelis> geordi: use static
<geordi> error: 'static' specified invalid for function 'x' declared out of global scope
<Eelis> geordi: use X x;
<geordi> error: base operand of '->' has non-pointer type 'X'
<Eelis> geordi: use x.f
<geordi> error: 'void X::f()' is private
<Eelis> geordi: use struct
<geordi> Success
<Eelis> geordi: show
<geordi> -c struct X { void f(); }; int main() { static X x; x.f(); }
Alternatively, these edits could have been specified with a single command:
<Eelis> geordi: use void f(); } and int main and static and X x; and x.f and struct
Geordi and the other users in the channel cannot read your mind, and the heuristics they use to determine which substring to replace are only "best effort". To prevent confusion (among geordi and the other users alike), it is therefore recommended to keep the modifications-to-context ratio of patterns low by being liberal with context, by not trying to simultaneously make multiple changes in a short code fragment, and by not trying to replace partial tokens (the heuristics have a strong bias in favour of whole-token replacements).
The "make" edit command changes properties (including type) of declarations:
<Eelis> geordi -c struct X { int i, j; virtual void f(X * p) const = 0; };
<geordi> Success
<Eelis> geordi, make f nonconst inline nonvirtual and make i a static reference to long and make p a const pointer to const
<geordi> Success
<Eelis> geordi, show
<geordi> -c struct X { long static int & i; int j; inline void f(const X *const p) ; };
Property descriptions generally follow the same syntax as with --make-type, with some additions.
"make" edit commands currently have a number of severe limitations:
BOOST_FOREACH(int & i)", because it looks like a function call, and "int & i" is not a valid function argument).int x; f(x);", the command "make x const" will produce "const int x; const f(x);" (because "f(x);" is parsed as a declaration of a variable named x).The implicitly included header prelude.hpp includes all standard library headers, some TR1 headers, several Boost headers, some namespace using's and aliases, and assorted miscellaneous utilities.
GCC bug 14940 prevents me from adding more TR1/Boost headers.
Descriptions of a few selected utilities follow.
Ranges (such as containers) and tuples have been made streamable:
<Eelis> geordi { vector<int> v { 3, 5, 9, 4, 1 }; cout << v; }
<geordi> [3, 5, 9, 4, 1]
See more_ostreaming.hpp for details.
Streaming TYPE<T> prints the type T.
TYPE(e) yields a string representation in C++ syntax of the static type of the expression e.
TYPE_DESC works the same, but yields string representations in verbose English.
<Eelis> geordi << TYPE(&system) <geordi> int (*)(const char*)
<Eelis> geordi << TYPE_DESC< int(&(*)())[5] >
<geordi> pointer to a function taking no arguments and
returning a reference to an array of 5 integers
See type_strings.hpp for details.
tracked::B is a simple class whose constructors, destructor, assignment operator, etc. print a short message when called. Objects of type tracked::B are also kept track of in a central registry, and leaks are reported if the program exits while tracked::B objects are still around. For example:
<Eelis> geordi { using tracked::B; B x (3), * y = new B;
swap(x, *y); y->f(); }
<geordi> B0*(3) new(B) B1* B2*(B0) B0=B1 B1=B2 B2~
B1.f() B0~ || leaked: B1
See tracked.hpp for a full list of tracked operations and additional tracked classes.
Geordi overloads the comma-operator to make it easy to print comma-delimited values:
<Eelis> geordi << 3, "oi", sin(3.9) <geordi> 3, oi, -0.687766
A slightly less terse but more flexible alternative is del, which can be used either as a manipulator or in place of cout, and which lets one optionally specify the delimiter:
<Eelis> geordi { cout << del << 3 << sin(3.9) << "oi. ";
del("; ") << 9 << 'x' << true; }
<geordi> 3, -0.687766, oi. 9; x; 1
See delimited_ostream.hpp for details.
Geordi adds bin to the std::hex/std::oct/std::dec family of I/O manipulators, with the semantics you would expect:
<Eelis> geordi << showbase << bin << 38 <geordi> 0b100110
See bin_iomanip.hpp for details.
<Eelis> geordi { int x = 45, y = x * x; cout << SHOW(x), SHOW(y),
SHOW(y - x); }
<geordi> x = 45, y = 2025, y - x = 1980
The RANGE macro expands RANGE(r) to (boost::begin(r)), (boost::end(r)) , which saves some typing for common cases.
<Eelis> geordi { int a [40]; fill(RANGE(a), 6); cout << a[31]; }
<geordi> 6
(See Boost.Range.)
BARKBARK prints the name and signature of the function in which it appears:
<Eelis> geordi { f(3.2); f('x'); } void f(int) { BARK; } void f(double) { BARK; }
<geordi> f(double) f(int)
The execution of the compiler, assembler, linker, and resulting program is all ptraced to disallow most system calls, is done as user/group nobody in a minimal chroot, and is subjected to strict resource limits and a timeout. Additionally, geordi can be run in sandboxed environments like User-Mode Linux and Xen.
First, you can always run your own geordi instance (though setting up geordi can be a pain in the ass). See Download.
That said, if #foo is a serious programming channel on Freenode that is not just a place to hang out, /join #geordi and talk to Eelis (who hosts geordi prime), and he'll consider having geordi prime join #foo.
/join #geordi. Please, please, do not spam other channels!
NO! Geordi uses various costly compiler flags and runtime aids to help diagnose ill-behaved code, making any kind of performance measure meaningless.
You are quite welcome to try, but please do so in the #geordi channel.
On Freenode, /join #geordi and/or /msg Eelis. Alternatively, mail to <name-of-this-software>@contacts.eelis.net .
Only pass geordi snippets when you're trying to demonstrate something to somebody (except in #geordi, where you are free to experiment as much as you like).
If you make a mistake in your snippet when trying to demonstrate something, give yourself at most one more attempt to get it right. If you fail to get your snippet right in your second attempt, then /join #geordi, work out your snippet there until it works, and then show it in the other channel.
Non-syntax-highlighted code with multiple statements all on a single line quickly becomes hard to read. Here are some tips to shave a few tokens off your snippets:
Use the shorthand syntaxes whenever possible. When you must use the "geordi: ..." syntax, don't list main's parameters if you don't need them. Finally, return 0; is implicit for main.
Don't use std:: qualification; namespace std has been using'd. Yes, this is normally considered poor style, but our unique spatial constraints justify its use for geordi.
Don't bother with endl or flush; geordi only outputs the first line anyway, and cout is flushed automatically at the end of the program.
Stuff values into containers using vector<int> v; v += 4, 3, 6, 2, 5; (this works thanks to Boost.Assign).
Use struct instead of class to avoid having to type public: for bases and members.
Geordi can be downloaded from github.
Since geordi just uses GCC, it inherits most of its problems. Some of the most annoying ones are listed below.
Some errors/warnings are truncated. This is due to a GCC bug that its developers don't feel like fixing.
Geordi wrongfully rejects standard-conforming code that happens to use certain identifiers in certain ways (e.g. you can't declare a variable named M_PI), and wrongfully accepts non-conforming code that uses certain identifiers in certain ways without having declared them. This is due to a GCC bug that prevents us from convincing GCC not to define various nonstandard things.
geordi { for(;;) ; }
Output: Killed
geordi { istringstream e ("2 4 * 3 2.1 + / 9 -"); stack<double> s; char c; while (e >> c) if (isdigit(c)) { e.putback(c); s.push(0); e >> s.top(); } else { double const x = s.top(); s.pop(); if (c == '+') s.top() += x; else if (c == '*') s.top() *= x; else if (c == '-') s.top() -= x; else if (c == '/') s.top() /= x; } cout << s.top(); }
Output: -7.43137
geordi << (3 * 0.1 == 0.3)
Output: false
geordi: bool comp (string const & a, string const & b) { return a.size() < b.size(); } int main () { using namespace boost::assign; vector<string> v; v += "superman", "spiderman", "batman", "hulk", "iceman", "wolverine"; sort(v.begin(), v.end(), comp); cout << v; }
Output: [hulk, batman, iceman, superman, spiderman, wolverine]
geordi { int const range = 1400000000, samples = 10000000; assert(range < RAND_MAX); int x = 0; for (int u = 0; u != samples; ++u) if ((rand() % range) < range / 2) ++x; cout << x / double(samples) * 100 << "% of samples lie in lower half of chosen range"; }
Output: 65.1787% of samples lie in lower half of chosen range
geordi -tw {char const*p=" weird yogurt craves most typical grayish germs covering my hyper hammer wheels arced underneath ugly cans ";while(*++p){uchar r=0;do r^=*p-'a'<<(*p&4);while(*++p!=' ');cout<<hex<<int(r)<<' ';}}
geordi: typedef double D;void plot(D(*f)(D),D ymin,D ymax,D xstart,D xstep,D xend){char c[]="_.,=-*'~`"; for (D x=xstart;x<=xend;x+=xstep){size_t h=size_t((f(x)-ymin)/(ymax-ymin)*sizeof(c));cout<<(h<sizeof(c)?c[h]:' ');}} int main() { plot(sin, -1, 1, 0, 0.3, 5 * M_PI); }
Output: *'~``~*-,.____.,=*'~``~*-,.____.,=*'~`'*
(Calculates 1+1 using Church numerals, producing a weak head normal form of 2. Uses de Bruijn indices.)
geordi -t <<m("(.(.(.(.3(210)0))))(.(.01))(.(.01))zs");tpd str S;tpd S::iterator I;int p;I h(I i){do p+=*i==40,p-=*i++==41;wh(p);ret i;}S r(I i,I e,int x,S a){if(i==e)ret"";I o=h(i);ret *i-40?(*i-x?S(1,*i):a)+r(i+1,e,x,a):'('+(i[1]-46?r(i+1,o-1,x,a):'.'+r(i+2,o-1,x+1,a))+')'+r(o,e,x,a);}S m(S);S m(I i,I e){S q(i,e);I j=h(i);ret *i-40?q:i[1]-46?m(m(i+1,j-1)+S(j,e)):j-e?m(r(i+2,j-1,48,S(j,h(j)))+S(h(j),e)):q;}S m(S x){ret m(RANGE(x));}
Output: s((.(.01))zs)
(In this snippet, X implements prefix increment, and obtains a canonically implemented postfix increment in terms of its prefix increment by inheriting postfix_incr<X>. Snippet based on Boost.Operators.)
geordi: template <typename Derived> struct postfix_incr { Derived * derived_this() { return static_cast<Derived *>(this); } Derived operator++(int) { Derived const t(*derived_this()); ++*derived_this(); return t; } }; struct X: postfix_incr<X> { int i; X & operator++() { ++i; return *this; } using postfix_incr<X>::operator++; }; int main() { X x; x.i = 2; x++; cout << x.i; }
Output: 3
geordi: { char y(34); stringstream i("geordi: { char y(34); stringstream i(!); string t; getline(i, t, '!'); cout << t << y << i.str() << y << i.rdbuf(); }"); string t; getline(i, t, '!'); cout << t << y << i.str() << y << i.rdbuf(); }
geordi: char program[]=">>,[>>,]<<[[-<+<]>[>[>>]<[.[-]<[[>>+<<-]<]>>]>]<<]", input[]="dicekjhbagfl", *i=input,m[512]={},*p=m;void b(char*c){for(;*c&&*c!=']';++c){(*((p+=*c=='>')-=*c=='<')+=*c=='+') -=*c=='-';*c=='.'&&cout<<*p;if(*c==',')*p=*i++;if(*c=='['){ for(++c;*p;)b(c);for(int d=0;*c!=']'||d--;++c)d+=*c=='[';}}}int main(){b(program);}
Output: abcdefghijkl
geordi: { int a[] = { 2, 3, 7, 1, 5, 6, 4, 0 }; quicksort(RANGE(a)); cout << a; } template <typename I> void quicksort(I const i, I const e) { if(i == e || boost::next(i) == e) return; I v(e); for(I u(boost::next(i)); u != v; ) if(*u <= *i) ++u; else swap(*u, *--v); swap(*i, *--v); quicksort(i, v); quicksort(++v, e); }
Output: [0, 1, 2, 3, 4, 5, 6, 7]
geordi: { int v[] = { 2, 3, 7, 1, 5, 6, 4, 0 }; while (!sorted(RANGE(v))) next_permutation(RANGE(v)); cout << v; } template <typename I> bool sorted (I b, I const e) { if (b == e) return true; I const c = b++; return b == e || (*c ≤ *b && sorted(b, e)); }
Output: [0, 1, 2, 3, 4, 5, 6, 7]
geordi { int * const p = new int[3]; for (int i = 0; i <= 3; ++i) p[i] = 8; delete[] p; }
Output: memory clobbered past end of allocated block
geordi { vector<int> s (3); cout << *(s.begin() + 4); }
Output: attempt to advance a dereferenceable (start-of-sequence) iterator 4 steps, which falls outside its valid range.
geordi { int i = 0; while (new (nothrow) char [1024 * 1024]) ++i; cout << i << " MiB"; }
Output: 188 MiB
geordi { ofstream f (__FILE__); string const meg (1024 * 1024, 'x'); for (;;) { f << meg << flush; cout << "+ " << flush; } }
Output: + + + + + File size limit exceeded
geordi: extern "C" int open (char const *, int); int main () { int i = 0; while (open(__FILE__, 0) != -1) ++i; cout << strerror(errno), i; }
Output: Too many open files, 23
geordi: { char buf [10]; fill(buf, buf+30, 'x'); }
Output: *** stack smashing detected ***: /t terminated
geordi: { for(;;) fork(); }
Output: SYS_clone: Operation not permitted