Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Viper 🐍➡️⚡

Viper is a source-to-source transpiler that converts typed Python into C++17.
Write Python with full type annotations, get clean, idiomatic C++ output — no runtime, no GC, no interpreter.


Usage

python viper.py source.py -o output.cpp
g++ -std=c++17 -O2 output.cpp -o output

Multiple source files are supported (merged into a single translation unit):

python viper.py main.py utils.py -o output.cpp

Requirements

  • Python 3.10+ (to run the transpiler)
  • A C++17-capable compiler (g++, clang++, MSVC)

Feature Reference

Legend: ✅ supported · ⚠️ partial / caveat · ❌ not supported


Primitive types

Python C++ Status
int int
float double
bool bool
str std::string
bytes / bytearray std::vector<uint8_t>
None (return) void

Collection types

Python C++ Status
list[T] std::vector<T>
dict[K, V] std::unordered_map<K, V>
set[T] std::unordered_set<T>
frozenset[T] std::unordered_set<T> (read-only)
tuple[A, B, ...] (heterogeneous) std::tuple<A, B, ...>
tuple[T, ...] (homogeneous) std::vector<T>
deque[T] std::deque<T>
defaultdict[K, V] std::unordered_map<K, V> (C++ operator[] provides default)
OrderedDict[K, V] std::map<K, V> (ordered by key)
Counter[T] std::unordered_map<T, int>

Indexing & slicing

Feature C++ Status
lst[i] (positive index) operator[]
lst[-1] / lst[-N] size() - N
lst[a:b] / lst[a:] / lst[:b] subrange copy
lst[::-1] / lst[a:b:step] step-slice lambda
lst[a:b] = new_list (slice assignment) erase + insert
s[i] (string char) std::string(1, s[i])
s[-1] / s[-N] negative char index
s[a:b] / s[a:] / s[:b] .substr()
s[::-1] (reverse string) std::string(rbegin, rend)
dq[i] (deque index) operator[]
obj[k] on user class calls __getitem__
obj[k] = v on user class calls set_item() (__setitem__)

Type system

Feature C++ Status
Optional[T] / T | None std::optional<T>
Optional[T] self-referential (e.g. nxt: Optional["Node"] inside Node) std::optional requires complete type; use pointer / shared_ptr instead
Union[X, Y] / X | Y std::variant<X, Y>
Callable[[A, B], R] std::function<R(A, B)>
TypeVar + generic functions template<typename T> function
Generic[T] classes template<typename T> class
ClassVar[T] static T + out-of-class definition
Type alias (MyList = list[int]) resolved at transpile-time
String forward references ("ClassName") resolved if class is known
isinstance(x, T) on Union/variant std::holds_alternative<T>
isinstance(x, T) on ordinary classes compile-time type walk
isinstance(x, T) runtime polymorphism ❌ needs virtual dispatch / RTTI
Any std::any + std::any_cast via cast(T, val) ⚠️ basic

Functions & decorators

Feature C++ Status
Typed functions direct translation
Default parameters direct translation
*args: T (variadic) std::vector<T> param
f(*lst) call unpacking expands to lst[0], lst[1], ...
**kwargs std::unordered_map<string,string> with empty default {} ⚠️ basic (string values only)
f(**dict) call unpacking Passes dict as kwargs argument to functions with **kwargs param
Keyword-only params (def f(*, x: int = 0)) regular C++ param with default
Keyword args in calls (f(y=3, x=4)) reordered to positional at compile-time
Nested functions (def inside def) auto f = [=](...) -> T { } (captures by value for safe return)
Functions as values (Callable) std::function<...>
Generator / yieldIterator[T] collects into std::vector<T>
@staticmethod static
@classmethod static (drops cls)
@property getter method name()
@x.setter method set_name(val)
@abstractmethod virtual ... = 0
@dataclass auto __init__, operator==, to_string(); default values, frozen=True, __post_init__ supported
@overload stubs skipped; actual implementation transpiled
Lambda with annotated args [&](T x) -> R { return ...; }
Lambda unannotated (lambda x: ...) Infers types from context (Callable annotation, function param) ⚠️ context-dependent
Lambda capture mode [&] for inline; [=] when stored as std::function (avoids dangle)
Lambda in map() / filter() Infers arg type from list element type
C++ keyword collision doubledouble_, autoauto_, etc. (appends _)

Control flow

Feature C++ Status
if / elif / else if / else if / else
while while
for x in range(...) for (int x = ...)
for x in list / set / deque / frozenset range-for
for x in dict for (auto& [x, _] : d)
for c in str char-by-char with std::string wrapper
for x in bytes for (uint8_t x : b)
for k, v in dict.items() structured binding
for i, v in enumerate(lst, start=N) index + element
for a, b in zip(l1, l2) index-based min-size
for a, b, c in zip(l1, l2, l3, ...) 3+ iterables
for x in reversed(lst) reverse iterators
for x in user_obj (with __iter__/__next__) while-loop with _StopIteration_
for x in heterogeneous tuple std::tuple is not iterable at runtime
for / while with else block after loop
break / continue direct
match / case (Python 3.10+) if / else if chain
Ternary x if c else y c ? x : y
Walrus := side-effect assignment + return
Chained comparisons 1 < x < 10 (1 < x && x < 10)
with (RAII / context manager) scoped block
with on user context manager __enter__/__exit__ + try/catch
with A() as a, B() as b: nested single-item with
List / dict / set comprehension lambda with push_back / insert
Multi-for comprehension nested loops

Classes & OOP

Feature C++ Status
Class with typed fields and methods class with public members
Single inheritance : public Parent
Multiple inheritance : public A, public B; MRO-style method lookup
super().__init__(args) Parent::Parent(args)
Parent.__init__(self, args) (Python 2-style super call) Parent::Parent(args)
super().method(args) Parent::method(args)
Virtual / override methods virtual / override
__init__ constructor
__del__ destructor ~ClassName()
__str__ / __repr__ to_string()
__eq__, __lt__, __le__, __gt__, __ge__, __ne__ operator==, <, etc.
__add__, __sub__, __mul__, __truediv__, __mod__ operator+, -, *, /, %
__iadd__, __isub__, __imul__, __itruediv__ operator+=, -=, *=, /=
__neg__, __pos__ unary operator-, +
__len__ size()
__bool__ explicit operator bool() const
__int__ / __float__ explicit operator int/double() const
__getitem__ T operator[](K key) const
__setitem__ void set_item(K key, V val)
__contains__ (x in obj) bool contains(T val) const
__hash__ _hash_() + std::hash<T> specialization
__iter__ / __next__ _iter_() / _next_() + sentinel loop
Enum / IntEnum enum class
StrEnum / string-valued Enum struct with static constexpr const char* members
IntEnumint compatibility (int)Enum::MEMBER cast; IntFlag also supported
NamedTuple struct with positional constructor
Inherited constructors using Parent::Parent; when subclass has no __init__
Polymorphic assignment (a: Base = Derived(...)) auto a = Derived(...) to avoid slicing
Polymorphic parameters (def f(a: Base)) const Base& reference; virtual methods auto-marked const if they don't mutate self
Protocol transparent base — structural typing NOT enforced ⚠️
__slots__ ❌ not needed (all fields declared explicitly)
@staticmethod / @classmethod static methods; callable as ClassName::method(...)

Exception handling

Feature C++ Status
try / except / else / finally try / catch
raise ExcType(msg) throw cpp_exc(msg)
raise (re-raise) throw
raise StopIteration() throw _StopIteration_{}
except (A, B) multiple catch blocks (body duplicated)
assert cond / assert cond, msg if (!cond) throw std::runtime_error(...)
Custom exception classes inherits std::exception subclass
Exception mapping ValueErrorstd::invalid_argument, IndexErrorstd::out_of_range, etc.

Operators & expressions

Feature C++ Status
+, -, *, /, //, %, ** direct / std::pow
&, |, ^, ~, <<, >> (bitwise) direct
not, and, or !, &&, ||
is None / is not None !has_value() / .has_value()
in / not in list / deque std::find
in / not in dict .count()
in / not in set / frozenset .count()
in / not in str .find() != npos
in / not in user class calls __contains__
str * int / int * str repeat string
list * int / int * list repeat list
list + list concatenation
Augmented assign (+=, -=, …) on numerics / str direct
Augmented assign on subscript (d[k] += v) direct
Augmented assign via user dunder (__iadd__ etc.) operator+= etc.
Set operators (|, &, -, ^, |=, etc.) lambda inline
User-class binary operators (__add__, __eq__, etc.) via operator overload

Built-in functions

Python C++ Status
print(...) std::cout << ...
print(..., sep=s, end=e) sep/end keyword args
len(x) (int)x.size()
range(n) / range(a,b) / range(a,b,s) for loop or std::vector<int>
abs(x) std::abs(x)
min(a, b) / max(a, b) std::min / std::max
min(lst) / max(lst) *std::min_element / *std::max_element
min(lst, key=f) / max(lst, key=f) custom comparator lambda
sum(lst) / sum(lst, start) accumulate lambda
any(lst) / all(lst) short-circuit lambda
sorted(lst) copy + std::sort
sorted(lst, key=f, reverse=True) key / reverse
reversed(lst) reverse iterators
enumerate(lst) / enumerate(lst, start=N) index + element
zip(l1, l2) / zip(l1, l2, l3, ...) min-size index loop
map(f, lst) transform lambda (named function or inline lambda)
filter(f, lst) filter lambda (named function or inline lambda)
input(prompt?) std::getline
int(x) / float(x) / str(x) cast / std::stoi / std::to_string
int(s, base) std::stoi(s, nullptr, base)
list(iterable) converts set/deque/frozensetvector; identity if already list
set(iterable) converts list/frozensetunordered_set; identity if already set
round(x) / round(x, n) std::round
pow(x, y) std::pow
chr(n) std::string(1, (char)n)
ord(c) (int)(c[0])
repr(obj) calls to_string() on user classes
isinstance(x, T) compile-time / holds_alternative
assert cond, msg if (!cond) throw
del d[k] .erase(k)
del x (Optional) x = std::nullopt
global / nonlocal no-op (scope access works)
type(x) ❌ requires RTTI
hasattr / getattr / setattr ❌ too dynamic
vars() / dir() ❌ too dynamic

String methods

Method C++ Status
.upper() / .lower() std::transform
.strip() / .lstrip() / .rstrip() find first/last non-whitespace
.split(sep) manual find loop
.split() (whitespace) std::istringstream
.join(lst) loop with separator
.replace(old, new) manual find+replace loop
.find(sub) .find() + npos → -1
.startswith(s) / .endswith(s) .compare()
.count(sub) find loop
.isdigit() / .isalpha() / .isspace() / .isalnum() std::all_of + ::is*
.isupper() / .islower() std::all_of + ::is*
.zfill(n) insert '0'
.ljust(n) / .rjust(n) / .center(n) pad with fill char
.removeprefix(s) / .removesuffix(s) compare + substr
.encode() std::vector<uint8_t> (ASCII/UTF-8)
.decode() on bytes std::string(begin, end)
f-strings {x}, {x:.2f}, {x:>10d}, {x:.Ne} std::ostringstream + iomanip
.format(...) (literal receiver only) same as f-string

List methods

Method C++ Status
.append(x) .push_back(x)
.pop() / .pop(i) .pop_back() / erase at index
.insert(i, x) .insert(begin()+i, x)
.extend(lst) .insert(end, ...)
.remove(x) std::find + .erase()
.clear() .clear()
.reverse() std::reverse in-place
.sort() std::sort in-place
.index(x) std::find → distance
.count(x) std::count
.copy() copy constructor
lst1 + lst2 concatenation
lst * n / n * lst repeat

Dict methods

Method C++ Status
d[k] read / write operator[]
.get(k, default) / .get(k) ternary .count() / std::optional
.setdefault(k, default) lambda + .count()
.update(other) loop merge
.pop(k) .erase(k)
.clear() .clear()
for k in d / .keys() structured binding
for v in d.values() structured binding
for k, v in d.items() structured binding
.keys() / .values() as list values lambda → std::vector
k in d / k not in d .count()

Set / frozenset methods

Method C++ Status
.add(x) .insert(x)
.remove(x) / .discard(x) .erase(x)
.clear() .clear()
.copy() copy constructor
.union() / .intersection() / .difference() / .symmetric_difference() lambda
.issubset() / .issuperset() / .isdisjoint() lambda
s1 | s2, s1 & s2, s1 - s2, s1 ^ s2 operator
s |=, s &=, s -=, s ^= augmented assign

Deque methods

Method C++ Status
.append(x) / .appendleft(x) .push_back / .push_front
.pop() / .popleft() .pop_back / .pop_front
.extend(lst) / .extendleft(lst) insert at end / push_front reversed
.remove(x) / .clear() std::find + erase / .clear()
.rotate(n) std::rotate
dq[i] subscript operator[]
.maxlen std::deque has no max-length; needs custom wrapper

Import system & stdlib stubs

Feature Status
Multi-file transpilation
Recursive import resolution
from x import y as z aliases
Graceful degradation (skip untranslatable items silently)
Stubs: math, random, time, sys, os
Stubs: typing, dataclasses, collections, enum
Stubs: itertools (chain, product)
Stubs: requests (GET/POST/PUT, Response)
itertools.chain(a, b) concat lists via lambda
itertools.product(a, b) nested loops → list[tuple[A,B]]
Third-party packages ❌ requires manual stub file

Skipped features

When a function or class can't be transpiled, Viper emits a // [Viper skip] comment in the C++ output explaining the reason and the source line number (e.g. line 5: missing parameter type annotation). No more silent drops.

Features still not supported:

Feature Reason
async / await Requires C++20 coroutines or a scheduler
isinstance() on runtime-polymorphic objects Requires virtual dispatch / RTTI
for x in heterogeneous tuple std::tuple is not runtime-iterable
type(x) Requires RTTI
hasattr / getattr / setattr Fully dynamic — no static equivalent
vars() / dir() Requires runtime introspection
__slots__ Irrelevant — Viper declares all fields explicitly
deque.maxlen std::deque has no capacity limit; needs custom type
Optional[T] self-referential (e.g. linked list node pointing to itself) std::optional<T> requires complete type; use raw/smart pointer instead
**kwargs with non-string values Only dict[str, str] (string→string) is supported
Custom decorators Only @dataclass, @staticmethod, @classmethod, @property, @abstractmethod are handled

Example

Input (example.py)

from typing import Generic, TypeVar

T = TypeVar('T')

class Stack(Generic[T]):
    items: list[T]

    def __init__(self) -> None:
        self.items = []

    def push(self, item: T) -> None:
        self.items.append(item)

    def pop(self) -> T:
        return self.items.pop()

    def is_empty(self) -> bool:
        return len(self.items) == 0

if __name__ == "__main__":
    s: Stack[int] = Stack[int]()
    s.push(1)
    s.push(2)
    s.push(3)
    while not s.is_empty():
        print(s.pop())

Output (example_out.cpp)

#include <iostream>
#include <string>
#include <vector>
// ...

template<typename T>
class Stack {
public:
    std::vector<T> items;

    Stack() {}

    void push(T item) {
        this->items.push_back(item);
    }

    T pop() {
        return [&]() -> T { auto _t = this->items.back();
            this->items.pop_back(); return _t; }();
    }

    bool is_empty() {
        return (int)this->items.size() == 0;
    }
};

int main() {
    Stack<int> s = Stack<int>();
    s.push(1);
    s.push(2);
    s.push(3);
    while (!s.is_empty()) {
        std::cout << s.pop() << std::endl;
    }
    return 0;
}

License

AGPL v3. If you use Viper in a network service or distribute it, you must release your source code.
Commercial licenses for proprietary use available upon request — contact mauronofrio (GitHub).

About

Typed Python to C++17 transpiler. Write Python with full type annotations, get clean zero-runtime C++ — no GC, no interpreter. Supports classes, generics, dataclasses, enums, lambdas, comprehensions, and 250+ stdlib features.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages