A shebang-friendly script for "interpreting" single C99, C11, and C++ files, including rcfile support.
For example, installing this ~/.c99shrc
control file
-Wall -g -O2
#include <stdio.h>
permits executing hello containing
#!/usr/bin/env c99sh
int main()
{
puts("Hello, world!");
}
to produce the output one expects provided c99sh is in the path. You
may also run c99sh foo.c
to execute some foo.c
lacking the shebang line. Try
c99sh -v foo.c
if you encounter trouble and want to see the compilation
command. Check out c99sh -h
for all the command line options you might use. In
particular, for simple tasks you might find that the command line options in
conjunction with HERE documents can accomplish many things. For example,
$ ./c99sh -sm <<HERE
puts("Hello, world!");
HERE
Control files can supply compilation and linking flags, preprocessor directives
like #include
, and
pkg-config directives to
simplify library usage. A c99shrc
located in the same directory as the
interpreted source will be used. Otherwise a ~/.c99shrc
is processed if
available. See c99shrc.example for an extended control file
enabling GSL,
GLib, and SQLite
capabilities. This permits quickly getting access to higher-level data
structures.
A more entertaining example is an OpenMP-enabled Monte Carlo computation of π screaming like a banshee on all your cores (c99shrc, source):
#!/usr/bin/env c99sh
int main(int argc, char *argv[])
{
long long niter = argc > 1 ? atof(argv[1]) : 100000;
long long count = 0;
#pragma omp parallel
{
unsigned int seed = omp_get_thread_num();
#pragma omp for reduction(+: count) schedule(static)
for (long long i = 0; i < niter; ++i) {
const double x = rand_r(&seed) / (double) RAND_MAX;
const double y = rand_r(&seed) / (double) RAND_MAX;
count += sqrt(x*x + y*y) < 1;
}
}
printf("%lld: %g\n", niter, M_PI - 4*(count / (double) niter));
}
Take that, GIL.
Kidding aside, the speedup in the edit-compile-run loop can be handy during
prototyping or analysis. It is nice when useful one-off scripts can be moved
directly into C ABI code instead of requiring an additional
{Python,Octave,R}-to-C translation and debugging phase. For example, compare
the Octave version of some simple logic with the
equivalent c99sh-based version requiring only a few
one-time additions to your ~/.c99shrc
.
As nearly the entire C99-oriented implementation works for C++, by invoking
c99sh through either a copy or symlink named cxxsh, you can
write C++-based logic. The relevant control files are named like cxxshrc
in
this case and they support directives like using namespace std
and namespace fb == foo::bar
. See cxx/hello and cxx/cxxshrc for
a hello world C++ example. One nice use case is hacking atop
Eigen since it provides pkg-config support. That
is, cxxsh -p eigen3 myprogram
builds and runs a one-off, Eigen-based program.
With the right cxxshrc
, such a program can be turned into a script. Though,
you will likely notice the compilation overhead much moreso with C++ than C99.
That said, for repeated invocation an output binary can be saved with the -x
option should repeated recompilation be prohibitively expensive. C11 can be
used via a symlink named c11sh with control files like c11shrc
.
The idea for c99sh
came from 21st Century
C's section "Compiling C
Programs via Here Document" (available
online)
by Ben Klemens. Additionally, I wrote it somewhat in
reaction to browsing the C++-ish work by
elsamuko/cppsh.