Showing posts with label cpp_type. Show all posts
Showing posts with label cpp_type. Show all posts

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.

std::integer_sequence


Hybrid algorithm, compile-time + runtime:
i.e. Linear search at compilation, binary search at runtime.

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.

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.


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:
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++:

  • Command
  • Composite
  • State
  • Interpreter
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.


Designing with types:

std::variant and std::optional are valuable tools that allow us to model
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.


Using types to constrain behavior:

"Phantom types" is one technique that helps us to model the behavior of
our 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 {};

New ways:
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:
  • 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