What is monomorphisation with context to C++?

C++Rust

C++ Problem Overview


Dave Herman's recent talk in Rust said that they borrowed this property from C++. I couldn't find anything around the topic. Can somebody please explain what monomorphisation means?

C++ Solutions


Solution 1 - C++

Monomorphization means generating specialized versions of generic functions. If I write a function that extracts the first element of any pair:

fn first<A, B>(pair: (A, B)) -> A {
    let (a, b) = pair;
    return a;
}

and then I call this function twice:

first((1, 2));
first(("a", "b"));

The compiler will generate two versions of first(), one specialized to pairs of integers and one specialized to pairs of strings.

The name derives from the programming language term "polymorphism" — meaning one function that can deal with many types of data. Monomorphization is the conversion from polymorphic to monomorphic code.

Solution 2 - C++

Not sure if anyone is still looking at this, but the Rust documentation actually does mention how it achieves no cost abstraction through this process. From Performance of Code Using Generics:

> You might be wondering whether there is a runtime cost when you’re > using generic type parameters. The good news is that Rust implements > generics in such a way that your code doesn’t run any slower using > generic types than it would with concrete types. > > Rust accomplishes this by performing monomorphization of the code that > is using generics at compile time. Monomorphization is the process of > turning generic code into specific code by filling in the concrete > types that are used when compiled. > > In this process, the compiler does the opposite of the steps we used > to create the generic function in Listing 10-5: the compiler looks at > all the places where generic code is called and generates code for the > concrete types the generic code is called with. > > Let’s look at how this works with an example that uses the standard > library’s Option enum: > > > > let integer = Some(5); > let float = Some(5.0); > > When Rust compiles this code, it performs monomorphization. During > that process, the compiler reads the values that have been used in > Option instances and identifies two kinds of Option: one is i32 > and the other is f64. As such, it expands the generic definition of > Option into Option_i32 and Option_f64, thereby replacing the > generic definition with the specific ones. > > The monomorphized version of the code looks like the following. The > generic Option is replaced with the specific definitions created by > the compiler: > > >
> // Filename: src/main.rs >
> enum Option_i32 { > Some(i32), > None, > } >
> enum Option_f64 { > Some(f64), > None, > } >
> fn main() { > let integer = Option_i32::Some(5); > let float = Option_f64::Some(5.0); > } > > Because Rust compiles generic code into code that specifies the type > in each instance, we pay no runtime cost for using generics. When the > code runs, it performs just as it would if we had duplicated each > definition by hand. The process of monomorphization makes Rust’s > generics extremely efficient at runtime.

Solution 3 - C++

Not sure about this; could you link to the talk? It might have been an offhanded remark.

Herman might have coined a term for something like template specialization, which generates types/objects which are mutually unrelated (not-polymorphic or "monomorphic") from the template, which is a polymorphic structure.

Solution 4 - C++

There is a nice explanation of monomorphization in the Rust Book

> Monomorphization is the process of turning generic code into specific code by filling in the concrete types that are used when compiled.

From the book example, if you have defined variables with Some:

> rust > let integer = Some(5); > let float = Some(5.0); > > > When Rust compiles this code, it performs monomorphization. During that > process, the compiler reads the values that have been used in Option<T> > instances and identifies two kinds of Option<T>: one is i32 and the other > is f64. As such, it expands the generic definition of Option<T> into > Option_i32 and Option_f64, thereby replacing the generic definition with > the specific ones. > > The monomorphized version of the code looks like the following. The generic > Option<T> is replaced with the specific definitions created by the compiler: > > Filename: src/main.rs > > rust > enum Option_i32 { > Some(i32), > None, > } > > enum Option_f64 { > Some(f64), > None, > } > > fn main() { > let integer = Option_i32::Some(5); > let float = Option_f64::Some(5.0); > } >

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionunj2View Question on Stackoverflow
Solution 1 - C++Niko MatsakisView Answer on Stackoverflow
Solution 2 - C++MichealView Answer on Stackoverflow
Solution 3 - C++PotatoswatterView Answer on Stackoverflow
Solution 4 - C++wolendranhView Answer on Stackoverflow