115 line brainfuck interpreter written in C++ This is a simple brainfuck interpreter I've written in C++. It takes a path to a brainfuck source file as a command line argument. The program uses a tape size of 30000 and cell size of one byte. Decrementing a cell's value under 0 will wrap it to 255 and vice versa.

I would really appreciate any kind of feedback, especially on how the existing code could be improved.

I'm using Apple Clang/LLVM and I've been compiling with the command:

clang++ -pedantic -O3 -std=c++17 brainfuck.cpp -o brainfuck


brainfuck.cpp:

#include &lt;algorithm&gt;
#include &lt;fstream&gt;
#include &lt;iostream&gt;
#include &lt;string&gt;
#include &lt;vector&gt;


/* Read the source file into a char vector, and return the vector. */
std::vector&lt;char&gt; read_source(std::string&amp;&amp; file_path)
{
    std::ifstream file(file_path);

    std::vector&lt;char&gt; source;

    char c;
    while (file.get(c)) {
        source.push_back(c);
    }

    return source;
}


/* Remove all but chars, which don't have meaning in brainfuck, from the source file.
 * This removes whitespace, newlines and comment chars. */
void filter_chars(std::vector&lt;char&gt;&amp; source)
{
    auto new_end = std::remove_if(source.begin(), source.end(),
                                  [](char c) { return !std::strchr("&lt;&gt;+-[],.", c); });

    source.erase(new_end, source.end());
}


/* The actual interpreter. Goes through the source char by char and
 * acts according to brainfuck's rules. */
void interpret(std::vector&lt;char&gt;&amp; source)
{
    std::vector&lt;unsigned char&gt; tape(30000, 0);
    int h = 0;  // the tape head

    int brace_count = 0;  // keeps track of nested braces

    int i = 0;  // the index to read the source
    while (i &lt; source.size()) {
        switch (source[i]) {
            case '&lt;':
                --h;
                break;
            case '&gt;':
                ++h;
                break;
            case '+':
                ++tape[h];
                break;
            case '-':
                --tape[h];
                break;
            case '.':
                std::cout &lt;&lt; tape[h] &lt;&lt; std::flush;
                break;
            case ',':
                std::cin &gt;&gt; tape[h];
                break;

            case '[':
                if (tape[h] == 0) {
                    ++brace_count;
                    while (source[i] != ']' || brace_count != 0) {
                        ++i;
                        if (source[i] == '[') {
                            ++brace_count;
                        }
                        else if (source[i] == ']') {
                            --brace_count;
                        }
                    }
                }
                break;

            case ']':
                if (tape[h] != 0) {
                    ++brace_count;
                    while (source[i] != '[' || brace_count != 0) {
                        --i;
                        if (source[i] == ']') {
                            ++brace_count;
                        }
                        else if (source[i] == '[') {
                            --brace_count;
                        }
                    }
                }
                break;

        }
        ++i;
    }
}


int main(int argc, char* argv[])
{
    if (argc &gt; 1) {
        std::vector&lt;char&gt; source = read_source(argv[1]);
        filter_chars(source);
        interpret(source);
        return 0;
    }
    else {
        std::cout &lt;&lt; "Expected a brainfuck source file as a command line argument.\n";
        std::cout &lt;&lt; "Terminating the program." &lt;&lt; std::endl;
        return 1;
    }
}
I see some things that may help you improve your program.

Use all of the required #includes

The function std::strchr is used but its declaration is in #include &lt;cstring&gt; which is not actually in the list of includes.

Be careful with signed and unsigned

In the interpret routine, the code compares an int i to a size_t (because that's what is returned from source.size()) but size_t is unsigned and int is signed.  Instead, declare i as size_t.

Copy and filter in one step

It's not wrong, by any means, but I'd be inclined to copy and filter all at once rather than as two separate steps.  Here's one way to do that:

std::vector&lt;char&gt; source; 
std::copy_if(std::istream_iterator&lt;char&gt;(std::cin), std::istream_iterator&lt;char&gt;(), 
             std::back_inserter(source), 
             [](char c) { return std::strchr("&lt;&gt;+-[],.", c); }
            );


It's not critical, of course, but this way it minimizes the number of times the vector is resized.  Also note that I've altered the program to read from std::cin instead of a file.  This way, it's possible to take input from a unnamed pipe, for example, adding to the flexibility of the program.

Fix the bugs

The current implementation doesn't check to see if it runs off the end of the vector while processing a brace and it doesn't check to see that h doesn't go out of bounds.