Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I write a c++ program that will compile itself by being run from the shell?

Tags:

c++

bash

shell

I often want to try things out in c++ without going to the trouble of writing a Makefile, creating a project or typing in complex command lines.

I wondered whether it's possible to make a .cpp file which is also a bash script, so it can compile and run itself.

I also wanted to be able to specify command line options in the script, in case there were dependencies like boost and so on.

like image 715
Richard Hodges Avatar asked Jan 07 '23 05:01

Richard Hodges


2 Answers

Rather than making a script that is also a cpp file that compiles itself, why not just make a script that watches the cpp file (or dir it is in) and re-compiles the cpp file on any new changes? inotifywait was made for that. While this doesn't precisely meet your question requirements, it will keep your code from carrying around all the script baggage. Example:

.cpp file

#include <iostream>

using namespace std;

int main (void) {
    cout << endl << "Code Running (ver. 10)" << endl << "and being timed." << endl;
    return 0;
}

inotifywait Script

#!/bin/bash

# watchdir=${1:-$PWD}
src=${1:-app.cpp}   ## set the source and output (exe) names
out=${src%.cpp}

while inotifywait -q -e modify -e modify -e close_write -e delete "$PWD"; do

    printf "\n compiling 'g++ %s -o %s'\n\n" $src $out

    [ -f $out ] && rm $out      ## remove existing exe before building

    g++ -o $out $src            ## build new exe

    [ -f $out ] || {            ## validate new exe created
        printf "error: compilation failed. exiting script.\n\n"
        printf "usage:   %s source.cpp (default: app.cpp)\n\n"
        exit 1
    }

    [ -x $out ] || {            ## validate it is executable
        printf "error: file produced from compilation is not executable.\n"
        exit 1
    }

    time ./$out 2>&1                        ## compute elapsed time
    exesz=$(du -h $out | awk '{print $1}')  ## store disk usage (removing name)

    ## print results
    printf "\n Source file:  %s\n" "$src"
    printf " output file:  %s\n" "$out"
    printf " size of exe:  %s bytes\n\n" "$exesz"

done

You can either run the script in the foreground or background. e.g.:

Example (output on app.cpp save)

$ ./watch.sh
/home/david/scr/tmp/stack/dat/tmp/bld/ MODIFY app.cpp

 compiling 'g++ app.cpp -o app'


Code Running (ver. 10)
and being timed.

real    0m0.003s
user    0m0.001s
sys     0m0.001s

 Source file:  app.cpp
 output file:  app
 size of exe:  16K bytes
like image 159
David C. Rankin Avatar answered Jan 21 '23 19:01

David C. Rankin


... it turns out that it is possible:

/*/../bin/ls > /dev/null
 filename=$(basename $BASH_SOURCE)
 dirname=$(cd $(dirname $BASH_SOURCE) && pwd)
 if [ "${dirname}" == "$(pwd)" ]
 then
    mkdir -p ${dirname}/bin > /dev/null
    filename="${dirname}/bin/${filename}"
 else
    filename="./${filename}"
 fi
 filename=${filename%.*}
 if [ $0 -nt ${filename} ]
 then
        c++ -o "${filename}" -std=c++1y $BASH_SOURCE || exit
 fi
 ("${filename}" && exit) || echo $? && exit
 exit
*/
#include <iostream>

using namespace std;

auto main() -> int
{
        cout << "Hello, World" << endl;
        return 0;
}

In this case I have named the file skeleton.cpp:

$ chmod +x skeleton.cpp
$ ./skeleton.cpp
Hello, World
like image 20
Richard Hodges Avatar answered Jan 21 '23 19:01

Richard Hodges