Reference:
https://howardhinnant.github.io/TypeHiearchy.pdf
Showing posts with label cpp_type. Show all posts
Showing posts with label cpp_type. Show all posts
Nov 22, 2024
Jul 15, 2023
[C++] unsigned char and std::byte under indeterminate byte read is not UB
https://en.cppreference.com/w/cpp/language/default_initialization
https://en.cppreference.com/w/cpp/types/byte
int f(bool b)
{
int x; // OK: the value of x is indeterminate
int y = x; // undefined behavior
unsigned char c; // OK: the value of c is indeterminate
unsigned char d = c; // OK: the value of d is indeterminate
int e = d; // undefined behavior
return b ? d : 0; // undefined behavior if b is true
}
Jun 13, 2022
[C++][CPPCON] s.t. about type kinds
Reference:
https://www.youtube.com/watch?v=va9I2qivBOA
std::variant
all_of
any_of
mismatch
equal
merge
set_union
set_intersection
Packs are a distinct kind
All types belong to a kind
... or 14(types), depending how you count
e.g.
nullptr
template names belong to another kind
There is a one-of-a-kind construct
Adding new kinds is almost unprecedented.
Packs are unlike everything else in C++
pointer to member function,
i.e. ->* .* return has no type.
Hybrid algorithm, compile-time + runtime:
i.e. Linear search at compilation, binary search at runtime.
Labels:
algorithm,
cpp,
cpp_algorithm,
cpp_type,
cppcon_2021
Mar 23, 2019
[Go] interesting read of Eli Bendersky's "Does a concrete type implement an interface in Go?"
Eli Bendersky has post an interesting article titled
"Does a concrete type implement an interface in Go?"
I really do love seeing C++ experts playing with other languages from their deep understanding of C/C++/Assembly :-)
Go's interface stores embedded data's type information, RTTI in C++, a pointer to v-table minus offset is type information, plus offset are pointers to member functions(index honors declared sequence).
Go's interface stores embedded data act as this pointer in C++, while calling the member functions, copying the embedded data as first parameter into member functions.
Thus, if embedded data is pointer type, then copy the pointer into the member functions.
Alas, if embedded data is instance of the data type, then copy the instance into the member functions.
Go's interface implement:
https://github.com/golang/go/blob/56131cbd1d61ec446e10dfe72a96f329ed3d952a/src/go/types/type.go#L244
Thus, Go's interface type provides an extra layer of abstraction for interface exchanges.
"Does a concrete type implement an interface in Go?"
I really do love seeing C++ experts playing with other languages from their deep understanding of C/C++/Assembly :-)
Go's interface stores embedded data's type information, RTTI in C++, a pointer to v-table minus offset is type information, plus offset are pointers to member functions(index honors declared sequence).
Go's interface stores embedded data act as this pointer in C++, while calling the member functions, copying the embedded data as first parameter into member functions.
Thus, if embedded data is pointer type, then copy the pointer into the member functions.
Alas, if embedded data is instance of the data type, then copy the instance into the member functions.
Go's interface implement:
https://github.com/golang/go/blob/56131cbd1d61ec446e10dfe72a96f329ed3d952a/src/go/types/type.go#L244
Thus, Go's interface type provides an extra layer of abstraction for interface exchanges.
package main
import "fmt"
type fun struct {
}
func (f *fun) run() {
fmt.Println("stateless member function call.")
}
type I interface {
run()
}
func main() {
I((*fun)(nil)).run()
}
C++: https://godbolt.org/z/YWAXze
#include <iostream>
struct fun {
void run()
{
using std::cout;
using std::endl;
cout << "stateless member function call." << endl;
}
};
int main()
{
static_cast<fun*>(nullptr)->run();
}
Reference:
A book about the internals of the Go programming language
https://github.com/teh-cmc/go-internals
Go 1.1 Function Calls - Russ Cox, February 2013
https://docs.google.com/document/d/1bMwCey-gmqZVTpRax-ESeVuZGmjwbocYs1iHplK-cjo/pub
Go Data Structures: Interfaces, Russ Cox, December 1, 2009.
https://research.swtch.com/interfaces
Go Interfaces, Ian Lance Taylor
https://www.airs.com/blog/archives/277
[golang] reflection quick note
http://vsdmars.blogspot.com/2019/01/golang-reflection-quick-note.html
Jan 5, 2019
[Design][Software Engineering][C++] Using/Design type effectively - Ben Deane@Blizzard
"On the whole, I'm inclined to say that when in doubt, make a new type."
– Martin Fowler, When to Make a Type
"Don't set a flag; set the data."
– Leo Brodie, Thinking Forth
Considering this talk provides an essential abstraction design for Type.
Yet Golang's multiple variables return programming paradigm should design as follows the concept of considering them as a whole into sum type.
If types' value domain are same, we could consider they are equivalent.
(But not 'equality')
Algebraically, a type is the number of values that inhabit it.
e.g.
How many values?
we multiply the # of inhabitants of the components.
This kind of compounding gives us a product type.
e.g
How many values?
we add the # of inhabitants of the components.
This kind of compounding gives us a sum type.
e.g.
How many values?
When we have a function from A to B,
we raise the # of inhabitants of B to the power of the # of inhabitants of A.
Curring, foundation of Lambda Calculus : https://en.wikipedia.org/wiki/Currying
e.g.
How many values?
– Martin Fowler, When to Make a Type
"Don't set a flag; set the data."
– Leo Brodie, Thinking Forth
Considering this talk provides an essential abstraction design for Type.
Yet Golang's multiple variables return programming paradigm should design as follows the concept of considering them as a whole into sum type.
Types as sets of values:
Type, like math's function, defines value domain.If types' value domain are same, we could consider they are equivalent.
(But not 'equality')
Algebraically, a type is the number of values that inhabit it.
e.g.
How many values?
bool; // 2, true, false
char; // 256
void; // 0
struct Foo {}; // 1
enum FireSwampDangers : int8_t { // 3
FLAME_SPURTS,
LIGHTNING_SAND,
ROUSES
};
template <typename T> // as many values as T
struct Foo {
T m_t;
};
Aggregating Types:
When two types are "concatenated" into one compound type,we multiply the # of inhabitants of the components.
This kind of compounding gives us a product type.
e.g
How many values?
std::pair<char, bool>; // 256 * 2
struct Foo { // 256 * 2
char a;
bool b;
};
std::tuple<bool, bool, bool>; // 2 * 2 * 2 = 8
template <typename T, typename U> // (# of values in T) * (# of values in U)
struct Foo {
T m_t;
U m_u;
};
Alternating Types:
When two types are "alternated" into one compound type,we add the # of inhabitants of the components.
This kind of compounding gives us a sum type.
e.g.
How many values?
std::optional<char>; // 256 + 1
std::variant<char, bool>; // 256 + 2
template <typename T, typename U> // (# of values in T) + (# of values in U)
struct Foo {
std::variant<T, U>;
}
Function Types:
The number of values of a function is the number of different ways we can draw arrows between the inputs and the outputs.When we have a function from A to B,
we raise the # of inhabitants of B to the power of the # of inhabitants of A.
Curring, foundation of Lambda Calculus : https://en.wikipedia.org/wiki/Currying
e.g.
How many values?
bool f(bool); // 2^2 = 4
char f(bool); // 256 ^ 2
enum class Foo
{
BAR,
BAZ,
QUUX
};
char f(Foo); // 256 ^ 3
template <class T, class U> // U ^ T
U f(T);
The above definition gives us how to present equivalent type:
e.g.
Equivalence:
so that (more) illegal states are un-representable.
Let's using sum types (variant, optional) as well as product types (structs):
e.g
Old way:
New way:
Old way:
New way:
The addition of sum types to C++ offers an alternative formulation for some
design patterns.
State machines and expressions are naturally modeled with sum types.
the state of our business logic more accurately.
When you match the types to the domain accurately, certain categories of
tests just disappear. (Consider Data Oriented Design)
Fitting types to their function more accurately makes code easier to
understand and removes pitfalls.
The bigger the code-base and the more vital the functionality, the more
value there is in correct representation with types.
our business logic in the type system. Illegal behavior becomes a type error.
New ways:
Reference:
[golang][c++] padding https://vsdmars.blogspot.com/2018/09/golangc-padding.html
template <typename T>
struct Foo {
std::variant<T, T> m_v;
};
template <typename T>
struct Bar {
T m_t;
bool m_b;
};
Algebraic Datatypes:
- the ability to reason about equality of types
- to find equivalent formulations
- more natural
- more easily understood
- more efficient
- to identify mismatches between state spaces and the types used to
implement them - to eliminate illegal states by making them inexpressible
Making illegal states unrepresentable:
std::variant is a game changer because it allows us to (more) properly express types,so that (more) illegal states are un-representable.
Let's using sum types (variant, optional) as well as product types (structs):
e.g
Old way:
enum class ConnectionState {
DISCONNECTED,
CONNECTING,
CONNECTED,
CONNECTION_INTERRUPTED
};
struct Connection {
ConnectionState m_connectionState;
std::string m_serverAddress;
ConnectionId m_id;
std::chrono::system_clock::time_point m_connectedTime;
std::chrono::milliseconds m_lastPingTime;
Timer m_reconnectTimer;
};
New way:
struct Connection {
std::string m_serverAddress;
struct Disconnected {};
struct Connecting {};
struct Connected {
ConnectionId m_id;
std::chrono::system_clock::time_point m_connectedTime;
std::optional<std::chrono::milliseconds> m_lastPingTime;};
struct ConnectionInterrupted {
std::chrono::system_clock::time_point m_disconnectedTime;
Timer m_reconnectTimer;};
std::variant<Disconnected,
Connecting,
Connected,
ConnectionInterrupted> m_connection;
};
Old way:
class Friend {
std::string m_alias;
bool m_aliasPopulated;
...
};
New way:
class Friend {
std::optional<std::string> m_alias;
...
};
Thus, we have a new design pattern for modern C++:
(I would recommend Robert Nystrom's Game Programming Patterns)
- Command
- Composite
- State
- Interpreter
design patterns.
State machines and expressions are naturally modeled with sum types.
Designing with types:
std::variant and std::optional are valuable tools that allow us to modelthe state of our business logic more accurately.
When you match the types to the domain accurately, certain categories of
tests just disappear. (Consider Data Oriented Design)
Fitting types to their function more accurately makes code easier to
understand and removes pitfalls.
The bigger the code-base and the more vital the functionality, the more
value there is in correct representation with types.
Using types to constrain behavior:
"Phantom types" is one technique that helps us to model the behavior ofour business logic in the type system. Illegal behavior becomes a type error.
e.g.
Old ways:
std::string GetFormData();
std::string SanitizeFormData(const std::string&);
void ExecuteQuery(const std::string&);
template <typename T>
struct FormData {
explicit FormData(const string& input) : m_input(input) {}
std::string m_input;
};
struct sanitized {};
struct unsanitized {};
FormData<unsanitized> GetFormData();
std::optional<FormData<sanitized>>
SanitizeFormData(const FormData<unsanitized>&);
void ExecuteQuery(const FormData<sanitized>&);
Total functions:
- A total function is a function that is defined for all inputs in its domain.
- Writing total functions with well-typed signatures can tell us a lot about functionality.
- Using types appropriately makes interfaces unsurprising, safer to use and harder to misuse.
- Total functions make more test categories vanish.
- Effectively using types can reduce test code.
Name this function:
(having lambda calculus knowledge is essential to understand what's going on next)template <typename T>
T f(T);
// identity
// int f(int);
template <typename T, typename U>
T f(pair<T, U>);
// first
template <typename T>
T f(bool, T, T);
// select
template <typename T, typename U>
U f(function<U(T)>, T);
// apply or call
template <typename T>
vector<T> f(vector<T>);
// reverse, shuffle, ...
template <typename T>
optional<T> f(vector<T>);
template <typename T, typename U>
vector<U> f(function<U(T)>, vector<T>);
// transform
template <typename T>
vector<T> f(function<bool(T)>, vector<T>);
// remove_if, partition, ...
template <typename K, typename V>
optional<V> f(map<K, V>, K);
// lookup
template <typename T>
T f(vector<T>);
// Not possible! It's a partial function - the vector might be empty.
// T& vector<T>::front();
template <typename T>
T f(optional<T>);
// Not possible!
template <typename K, typename V>
V f(map<K, V>, K);
// Not possible! (The key might not be in the map.)
// V& map<K, V>::operator[](const K&);
Take away:
- Make illegal states unrepresentable
- Use std::variant and std::optional for formulations that are
- more natural
- fit the business logic state better
- Use phantom types for safety
- Make illegal behavior a compile error
- Write total functions
- Unsurprising behavior
- Easy to use, hard to misuse
Reference:
[golang][c++] padding https://vsdmars.blogspot.com/2018/09/golangc-padding.html
Sep 21, 2016
[C++] Opaque Typedef library
video:
https://www.youtube.com/watch?v=jLdSjh8oqmE
library:
https://sourceforge.net/p/opaque-typedef/wiki/Home/#opaque-typedef-library
Reference:
Toward Opaque Typedefs for C++1Y, v2 (PDF)
[C++][NOTE][ORIGINAL] Strong typedef
Microprocessors have kinds of addresses:
What kind of address am I talking about?
Opaque typedef
Idea:
code:
Merit:
https://www.youtube.com/watch?v=jLdSjh8oqmE
library:
https://sourceforge.net/p/opaque-typedef/wiki/Home/#opaque-typedef-library
Reference:
Toward Opaque Typedefs for C++1Y, v2 (PDF)
[C++][NOTE][ORIGINAL] Strong typedef
Microprocessors have kinds of addresses:
- Virtual address
- Linear address
- Guest physical address
- Host physical address
- DDR address
What kind of address am I talking about?
Opaque typedef
Idea:
- Wrap a variable of some type in a new type
- Mimic the interface of the original type, but using the new type
code:
struct linaddr : numeric_typedef<uint64_t, linaddr>
{
using base = numeric_typedef<uint64_t, linaddr>;
using base::base;
}
Merit:
- Safer interfaces by removing implicit convertibility
- Makes overloading on the new type possible
- Turn semantic bugs into compile time errors
Subscribe to:
Posts (Atom)


