viper_
A fun single-header library that makes C++ feel just like Python
Tech Stack
About viper_
viper_ is a single-header library for modern C++ that provides a similar workflow to Python. viper_ is NOT a Python interpreter or language binding. It simply uses real C++ syntax to emulate Python’s core features.
Goals
-
Emulate the feeling of Python: Support the most fundamental Python features.
-
Use only real C++ syntax: I started this project to challenge myself to work around the constraints of the language. I will use as many interesting or esoteric C++ features as it takes to make it work.
-
Stay as close as possible to Python’s syntax: Python will never be valid C++, so the syntax can’t be exactly the same. However, when I must deviate from Python syntax, I will do so as minimally and consistently as possible.
-
Seamless interoperability: Must be able to easily call upon viper_ functionality from C++ and vice versa without extra setup.
Restrictions
- No interpreting entire strings of Python: Doing so would defeat the entire point of using C++ syntax by any means necessary. However, short strings for individual things like variable/function names are allowed.
Why the underscore?
The reason viper_ has the underscore is because it was born with the creation of the operator ""_ (see the next section), after which the underscore became the default for syntax that had to deviate from Python.
Current Features
Variables
Variables are strongly, dynamically duck-typed
# Python
my_var = 10 # Declare
my_var = "Hello!" # Reassign
print(my_var) # Access
# >> Hello!
With viper_, add quotes around and an underscore after variable names (string literal operator ""_)
// C++
"my_var"_ = 10; // Declare
"my_var"_ = "Hello!"; // Reassign
print("my_var"_); // Access
// >> Hello!
Note that
"Hello!"doesn’t have the underscore because it’s a regular string and not a variable name like"my_var"_is.
Format Strings
Format strings allow you to easily insert variable values into text using their names
# Python
text = "World"
formatted = f"Hello, {text}!" # Refers to the variable called text
print(formatted)
# >> Hello, World!
With viper_, use the same f"" syntax:
// C++
"text"_ = "World";
"formatted"_ = f"Hello, {text}!"; // Refers to the variable called text
print("formatted"_);
Note that the
""_is not necessary for variable names inside the format string
Functions
# Python
def not_equal_to(x, y):
return x != y
result = not_equal_to(1, 2) # True
With viper_, still use def but wrap the function name in parenthesis and wrap the code in curly braces folled by a semicolon
// C++
def (not_equal_to)("x"_, "y"_) {
return "x"_ != "y"_;
};
"result"_ = not_equal_to(1, 2); // True
The curly braces and semicolon
{...};are required because viper_ functions are technically C++ lambda declarations. This was necessary because C++ functions can’t be declared inside other functions, so they needed to be actual C++ variables.
The
()around the function name was the best way to hide the capture[]of the lambda which otherwise would’ve required the developer to specify their own capture which would’ve felt strange from both C++ and Python perspectives.
*args can be used to allow any number of positional arguments to be passed to a function
# Python
def not_equal_to(x, *args):
for arg in args:
if x == arg:
return False
return True
result = not_equal_to(1, 5, 3, 1) # False
With viper_, it works the same
# C++
def (not_equal_to)("x"_, *"args"_) {
for ("arg"_ in "args"_)
if ("x"_ == "arg"_)
return False;
return True;
};
"result"_ = not_equal_to(1, 5, 3, 1); // False
- End of Documentation -
In viper_ but currently undocumented here:
- **kwargs keyword arguments
- naming arguments when calling a function
- unpacking with * and **
- extension: variables named with numbers