# Writing a Compile-Time CSV Parser in C++, Part 1: Constexpr Overview

# Introduction

In part 1 of the article I discuss the `constexpr` feature as it's specified in C++17 and C++20.

In [part 2](https://ashaduri.hashnode.dev/writing-a-compile-time-csv-parser-in-c-part-2-the-parser) I describe the steps I took to write a library for compile-time (and runtime) parsing of CSV files called [Csv::Parser](https://github.com/ashaduri/csv-parser):

%[https://github.com/ashaduri/csv-parser]


### What Do I Mean by Compile-Time?

One of the major strengths of C++, like other compiled-to-native-code languages, is speed. C++ achieves it by compiling the source code to fast, optimized, CPU-native machine code.

While the first ISO standard of C++ (C++98) is a fast language, many developers think that there is more room for efficiency. Move operations, "as if" rule for optimizing out memory allocations, and guaranteed copy elision introduced in later C++ standards are some of the new features which improve the performance of existing code "for free".

However, the quickest calculation is the one that does not happen at all, or, more precisely, happens in advance during compilation.

Templates always provided the standard C++ with some rudimentary capability of compile-time calculation. However, C++11 introduced a whole new feature called `constexpr` which made compile-time calculation easier, but still tricky and difficult to reason about due to its limitations. C++17 removed most of these limitations and now a lot of ordinary source code can be easily evaluated during compilation, freeing up valuable computational resources on the user's machine.

# What `constexpr` Does

For sake of simplicity, I will mainly focus on `constexpr` as it's defined in C++17. Pre-C++17 `constexpr` is quite limited and not many of C++20 `constexpr` features have been fully implemented as of GCC 11 / Clang 11.

Marking a function or a variable `constexpr` declares that it is *possible* to evaluate the value of the function or variable at compile time. Such values can then be used where only compile-time constant expressions are allowed. Marking a variable declaration `constexpr` also implies `const`. *(Note: `constexpr` methods used to be `const` by default, but this was removed in C++14).*

The syntax for making a function or a variable `constexpr` is:
```C++
constexpr int func()
{
    // ...
}

// must be initialized by a constant expression
constexpr int variable = 1;  
```

### Compile-Time or Runtime?

It is important to understand that simply marking a function `constexpr` does not mean it will be evaluated during compilation. Whether such functions are evaluated at compile-time or runtime depends on a few things:

- If the result of the function must be available at compile-time as a constant expression, the function is evaluated at compile-time. For example, assigning the function result to a `constexpr` variable or using it as a non-type template argument forces compile-time evaluation. Of course, for this to work, all the arguments of the `constexpr` function must be constant expressions as well. Note that if such evaluation is impossible (because, for example, a function argument value is unavailable at compile-time), you will get a compilation error.

- If the function depends on runtime values, it will be evaluated at runtime.

- If the function depends on compile-time values, but its result is not required to be a constant expression, it is up to the compiler to decide whether it will run the evaluation at compile-time or not. Current compilers fall back to runtime evaluation in this case.

C++20 Standard Library provides a function [std::is_constant_evaluated](https://en.cppreference.com/w/cpp/types/is_constant_evaluated) to help you find out whether the evaluation is happening at compile-time or runtime.

### A Note on `constexpr` Lambdas

Unlike regular functions, lambda functions are automatically `constexpr` if the compiler determines that they can be. It may still be useful to mark them explicitly with `constexpr` keyword to force them to adhere to `constexpr` rules:

```C++
auto lambda = [](int argument) constexpr
{
    // only the operations valid in constexpr context are allowed here
};
```

### Forcing Compile-Time Evaluation

The only way to guarantee that a `constexpr` function will be evaluated at compile-time is to use its result as a constant expression. There are a few ways to do this in C++:

- Assign it to a `constexpr` variable:
```C++
constexpr auto result = func();
```

- Provided its result can be used as a non-type template argument, you can do the following:
```C++
template <int N>
constexpr void forceCompileTime() { }

forceCompileTime<func()>();
```
Or something like:
```C++
template <int N>
struct ForceCompileTime {
    static const int value = N;
};

auto value = ForceCompileTime<func()>::value;
```

- If you just want to call a few functions at compile-time, you could use `constexpr` lambda:
```C++
[[maybe_unused]] constexpr auto result = [&]() constexpr {
    func1();
    func2();
    return true;  // or perhaps return func3();
}();
```


# Constexpr Functions - What Can I Write Inside Them?

`constexpr` functions are limited in what they can do. Here are a few of these limitations (as of C++17 and C++20):

- A `constexpr` function cannot call a non-`constexpr` function. This means that the C standard library is off limits.

- The definition of a `constexpr` function must be available when calling it. This is similar to function templates and `inline` functions (in fact, `constexpr` functions are implicitly `inline`). In practice, this means that the definitions should be placed in header files.

- All local variables, parameters, and the return value must be of a [Literal Type](https://en.cppreference.com/w/cpp/named_req/LiteralType)

- A `constexpr` function cannot be a coroutine.

- A `constexpr` function can be virtual, but only since C++20.

- `goto` is not allowed.

- Inside a `constexpr` function, declaration of a variable without initialization is allowed, but only since C++20. The same rule applies to `constexpr` variables.

- Since C++20, `try` blocks are allowed, but actually throwing an exception at compile-time is also prohibited, causing a compilation error.

- C++20 allows some use of `std::vector` and `std::string` in `constexpr` contexts, but neither GCC 11 nor Clang 11 implement it (yet).


### Testing and Error Reporting

One useful side effect of evaluating `constexpr` functions at compile-time is that we can also do some testing during compilation without actually running any unit tests. By catching the errors early on at compile-time, this approach increases the reliability and correctness of the code without being dependent on runtime tests.

For example, we can use `static_assert` in a test to verify correctness of a function at compile-time:
```C++
constexpr int add(int a, int b)
{
    return a + b;
}

static_assert(add(1, 2) == 3);  // OK

// The following line fails to compile: 
static_assert(add(1, 2) == 4);
```

We can also conditionally throw an exception to trigger a compilation error. For example, to verify that a precondition is met we can write:
```C++
constexpr int compute(int a, int b)
{
    if (a > b) {
        throw std::logic_error("Precondition failed");
    }
    return a + b;
}

[[maybe_unused]] constexpr int result1 = compute(1, 5);  // OK
[[maybe_unused]] constexpr int result2 = compute(50, 2);  // Fails to compile
```
Note that we cannot use `static_assert` in `compute()` because the parameters may or may not be constant expressions - `compute()` can be used in runtime context as well.

It is important to note that a compiler is not required to issue an error if a `constexpr` function contains a code path that can only be evaluated at runtime, but is not triggered with particular set of compile-time arguments. The conditional throwing of exception in the example above relies on this behavior.


# What's Next
In [part 2](https://ashaduri.hashnode.dev/writing-a-compile-time-csv-parser-in-c-part-2-the-parser) of the article I will talk about a way to approach writing a compile-time CSV parser, presenting a Csv::Parser library implemented using `constexpr`.


# Useful Links

- [constexpr reference](https://en.cppreference.com/w/cpp/language/constexpr)
- [Features supported by various C++ compilers](https://en.cppreference.com/w/cpp/compiler_support)
- [CppCon 2017: Ben Deane & Jason Turner “constexpr ALL the Things!” - a compile-time JSON parser](https://www.youtube.com/watch?v=PJwd4JLYJJY)
- [Csv::Parser at GitHub](https://github.com/ashaduri/csv-parser) 


###### *Copyright © 2021 Alexander Shaduri*

