Showing posts with label software_engineering. Show all posts
Showing posts with label software_engineering. Show all posts

Jun 13, 2026

[RACI]

The 4 RACI Roles

  • Responsible (The "Doer"): The person (or team) who actually performs the work to complete the task. They are hands-on and do the heavy lifting.
    Rule of thumb: There should be at least one 'R', but multiple contributors can share this responsibility.
  • Accountable (The "Owner"): The individual ultimately answerable for the correct and thorough completion of the deliverable. They delegate the work and sign off on the final result.
    Rule of thumb: There must be exactly one Accountable person per task to guarantee decision-making authority.
  • Consulted (The "Expert"): People who provide subject-matter expertise or input before a decision or action is finalized.
    Rule of thumb: Communication is two-way.
  • Informed (The "Loop"): People who need to be kept up-to-date on project progress, usually stakeholders or leadership.
    Rule of thumb: Communication is one-way—they are simply updated, not required to make decisions.

May 7, 2022

[CPPCON 2021][notes] Debugging/Developing techniques

Reference:
https://www.youtube.com/watch?v=M7fV-eQwxrY

Define bugs

  • System is subject to a set of requirements.
  • A software defect is a non-conformity to requirements.
  • Pre-Curr-Post condition violated.
  • A non-conformity is a failure to meet one or more requirements.
  • A defect is incorrect program data causes a non-conformity.
  • A symptom is observable evidence of a defect.
  • A deterministic defect is a defect that does not change its symptoms under a well-defined set of conditions.
  • In contrast, a non-deterministic defect is a defect that changes its symptoms from run-to-run under a well-defined set of conditions.


Terminology

  • A context is the totality of the environment is which a program that exhibits symptoms is running
  • A problem report describes one or more symptoms in some context
  • Analogous context is a replica of the original context
  • Lab is the setting that we have total control over the context
  • Field is the setting that we have minimal or no control over the context


Relationship

Problem report -> Symptoms <-> Defects


Challenges

  • Problem report can be unhelpful (feed back from the user)
  • Problem report may not indicate actual problem
  • Collecting program state data may be difficult (log/setting/dump)
  • Symptoms may not indicate the cause
  • Defects and symptoms change as repair progresses
  • Fixing one defect may introduce new defects (messy design/quick fix)
  • Symptoms can be difficult to reproduce


Debugging process

Tend to think debugging is a linear process; i.e.
  • Characterize and reproduce
  • Locate
  • Classify
  • Understand
  • Repair


In reality tips

  • Review problem report
  • Characterize and reproduce problem
  • Clone if possible
  • Reproduce problem (loop)
    • understand problem
    • locate problem
    • classify problem
    • gain insight
    • attempt to repair
  • Problem fixed; deliver



In detail

Characterizing

  • Determining the context in which symptoms were observed
  • Version number, platform, resources allocated, external interfaces, configuration data, etc.
  • Information that allows you to instantiate an analogous context


Reproducing

  • Instantiating an analogous context, in the lab, or in the field
  • Running enough of the program/system to observe the reported symptoms
  • Developing new/updating existing test assets to demonstrate the failure
  • Make sure looking at the correct source code.
Characterizing and reproducing a problem is vital to the debugging process.


Understanding

Gaining ENOUGH knowledge about a problem and the surrounding code, that you believe you can make changes to carry out a repair.

At a minimum

  • located the incorrect lines of code
  • why the code is incorrect, root cause?
  • check the proposed classification
  • formulated a set of proposed changes
  • determine how the proposed changes could affect the runtime state

Inspect and verify the associated test assets

  • The test cases or harnesses may be broken
  • Test data should demonstrate correct and incorrect behavior

The defect may not be where you expect it

  • Keep an open mind and be ready to question all parts of the program

Ask yourself where the defect is not

  • trying to prove the absence of a defect reveals the defect

Explain to people why there is a defect, and why your proposed fix will resolve the defect

  • A local guru or bobblehead could be helpful - reach out for help if necessary


Locate the problem

Employ good development practices at the outset

  • Practice iterative, incremental, bottom-up development
  • Add functionality in small sections of code
  • Create test assets for each new increment of functionality
  • Verify that new code doesn't cause previous test cases to fail
  • Verify that new code passes its own test cases
  • Practice defensive programming

Alas

  • Well-written and extensive test assets
  • Preferably the whole product does this, at a minimum your fixes should
  • Adds runtime overhead, which can hinder the search for non-deterministic problems

Use trace logging

  • Generating output describing the program state during execution
  • In simpler cases, instrument code with print statements
  • In more complex systems, take advantage of existing logging facilities

Alas

  • Great way to stay 'on the path' when developing new code
  • An easy first step in narrowing down a problem's scope

Use debugging and analysis tools

  • Compiler warnings
  • Static code analysis tools (cppcheck, etc.)
  • Interactive debugger(gdb, lldb, udb, etc.)
  • Time-travel debugger(gdb, rr, udb, etc.)
  • Sanitizers (asan, tsan, ubsan, etc.)
  • Dynamic program analyzers(valgrind, etc.)
  • tracers (strace, wireshark, etc.)

Alas

  • for deterministic problems
  • not always useful for non-deterministic problem

Enable and/or add assertions

  • verify pre/curr/post condition of a function call.
  • verify expected program state

Alas


Use backtracking

  • Try to understand the program state at each backward step

Alas

  • Good for very simple programs/small search with deterministic problems

Divide and conquire (binary search)

  • Pick section of code to examine
  • Place an assertion or set a breakpoint.
  • Repeat until reveals the defect

Problem simplification

  • Gradually and strategically remove/comment out sections of irrelevant code

Alas

  • useful for debugging crashes of release builds
  • work backwards from the end of the section

Make the problem worse

  • Magnifies the problem signal.

Alas

  • helpful in first step finding and understanding the problem

Scientific method

  • Form a hypothesis consistent with observations
  • Implement tests to refute the hypothesis
  • If refuted, form a new hypothesis with new tests

Alas

  • time consuming; especially for code base that unfamiliar with
  • effective for all problems


Problem Types

Deterministic problems

  • Review the logs
  • Add assertions
  • Use interactive debugger

Non-deterministic problems

  • Review the logs
  • Create a debug build and see if it also exhibits the same symptoms
  • Add assertions where needed to verify invariants
  • Add assertions; comment out code, divide-and-conquer
  • Make the problem WORSE to magnify the problem
  • try low-overhead debugging tools "$gcc -g -o2"



Steps

Classifying

  • Determining a defect's category
  • Useful in formulating a repair strategy
  • Important information in subsequent reviews when considering preventive actions

Syntax errors
Syntax warnings
Implementation errors
Logic errors
Configuration errors


Repairing the problem

  • Implementing the appropriate fixed.
  • Passing the tests
  • Tests should be well written
  • Minimize changes to the system - keep changes small and localized
  • Verify repairs against test assets
    • All new/update tests should pass
    • All other tests should pass

Delivery

  • Practice good version control
  • Don't include fixes for more than one problem in one commit
  • Don't include extraneous changes (e.g. new features) in fix commits
  • Include new/update test assets in the fix commits
  • Write commit comments clear and concise

Verify tests again

  • Double check all new/update tests pass
  • Double check all other tests pass

Create documentation for posterity

  • How the defect was noticed
  • The conditions under which the defect occurred - the context
  • Steps necessary to reproduce the defect - the analogous context
  • Techniques and tools used to localize the defect
  • Defect's category
  • Underlying root cause of the defect
  • Latent defects precluded by fixing this defect
  • Possible latent defects left unaddressed
  • Mistake made and recommendations for preventive actions



Developing new feature

  • Practice defensive programming
    • Assume the worst case could happen at any time.
  • Employ an appropriate iterative and incremental development process
    • Decide what needs to be achieved
    • Formulate a plan for the achievement
    • Understand the invariants, requirements, and context, then design the solution.
    • Implement the solution in small, discrete, testable chunks
    • Write code to verify invariants, pre-cur-post conditions and self-test complex components
  • Consider employing the principles of test-driven design
  • Employ good configuration management practice EVERYWHERE.

Apr 28, 2022

[C++][Algorithm][design] predicate callable logic for sorting (and any of the ordering function/algorithm call)

Reference:
https://danlark.org/2022/04/20/changing-stdsort-at-googles-scale-and-beyond/
https://vsdmars.blogspot.com/2018/06/c-regular-type.html

Concept:

Type:


Design by contract (link to defensive programming)

P: precondition
Q: operation
R: postcondition


Domain (as in math)

Domain of operation is used in the ordinary math sense to denote the set of values over which an operation is (required to be) defined.

This set can change over time. Each component may place additional requirements on the domain of an operation.

These requirements can be inferred from the uses that a component makes of the operation and are generally constrained to those values accessible through the operation's arguments.

Domain of the operation is NOT the types of the arguments.

 

Safety & Correctness

  • An operation is safe if it cannot lead to UB
    • directly or indirectly
    • even if the operation preconditions are violated
  • An unsafe operation may lead to UB if preconditions ever are violated
    • Either directly or during subsequent operations, safe or not
Code that violates preconditions is incorrect.


Requirements for correctness

  • A correctly implemented operation guarantees that:
    • If preconditions are satisfied
      • The operation will either succeed, result matches post conditions
      • Or report failure, return an error, thrown an exceptions, set errno etc.
      • Any objects being mutated by the operations must be left in a "known or determinable state"
        • A weaker requirement than valid
    • If preconditions is not satisfied
      • If the operation is safe
        • The result is unspecified which could include:
          • Failure
          • Trapping
          • Leaving any object being mutated by the operations in an unspecified, possibly invalid state.
      • If the operations is unsafe
        • The behavior is undefined(Full STOP)
Compiler can do /anything/ if there's an UB(stripping expressions etc.)

We could exploit contract to work for us; e.g. unsigned (contracted with mod(2^b))


Strong preconditions

  • Pros
    • flexibility of implementation
    • ascribe meaning and intent to an operation
    • simplify requirements and reasoning about code
  • Cons
    • limit clever uses that exploit otherwise defined behavior
    • allow for variance in behavior between implementations

ALWAYS refer to C++ ISO for contract of std::


A tl;dr take away for predicator of sorting

When calling any of the ordering functions including
  • std::sort
  • std::find
compare functions(aka predicate) much comply with the strict weak ordering which formally means the following:
  • Irreflexivity: x < x is false (strict partial order rule)
  • Asymmetry: x < y and y < x cannot be both true (strict partial order rule)
  • Transitivity: x < y and y < z imply x < z (strict partial order rule)
  • Transitivity of incomparability: x == y and y == z imply x == z, where x == y means x < y and y < x are both false (equivalence relations on incomparable elements rule)
Above conditions are used for optimization purposes and an abide by is a must for code correctness.



Jan 7, 2019

[quote] define Perfection.

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. -- Antoine de Saint-Exupery

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

Jan 3, 2019

[C++] linked to previous post: "thoughts on Aras's "Modern" C++ Lamentations"

https://sean-parent.stlab.cc/2018/12/30/cpp-ruminations.html

quoted:
Programming is a profession. It is an ethical obligation to work to improve our profession. The more senior and talented you are, the more you owe to the community. Giving back can take many forms; mentoring, lecturing, publishing, serving on committees and furthering open source projects. Part of that obligation is to continue to study, to read papers and work through books. Not knowing the history of iota() should not be something to be proud of, but an embarrassment.

The C++ standards committee is filled with people with diverse interests. Some who make their living teaching the language and so want to be informed, some who represent companies and the interests of those companies, some from academia who are seeking publication, grants, and tenure. I don’t know of anyone on the committee who is primarily there to just collect feedback from the users of the language and try to incorporate it, except possibly Bjarne. The fact that many do this regardless, is out of their own sense of professionalism. If you want a stronger say in the future of the language, you will have to sit at the table.

I can agree with the stated goals of improving compilation time and debug run times. However, I also want to save time researching, designing, proving, reviewing, testing, and reading code. The range library is a pretty powerful tool for the later points. And yes, you can write horrible code with the range library and it will require some level of study to use well. As with mathematics, there is no royal road.

Linked to:
http://vsdmars.blogspot.com/2018/12/ph.html
https://mropert.github.io/2019/01/02/gamedev_intro_to_modern_cpp/
https://medium.com/@pat_wilson/get-your-shit-together-6ccbfd6bb755
Nice sum up:
http://lucasmeijer.com/posts/cpp_unity/

Dec 31, 2018

[C++] thoughts on Aras's "Modern" C++ Lamentations

Read: http://aras-p.info/blog/2018/12/28/Modern-C-Lamentations/

Classic example for not following KISS.

'range' library is fancy due to it pushes the limits of using almost every bits of the language features,
i.e unless being a C++ language lawyer could you debug the error spit out from the compiler..

From above blog post even C# version's range code example smells bad.
(from Python's point of view, duh~)

Not only 'using the right language for the problem', but also 'using the right form of coding within the language' is important.

When decided to start a project with C++, always identifies the subset C++ features that are allowed for the project, failed doing so will definitely lead to the project's failure, which including slow compile time, hard to debug, and even worse in performance(including time-to-deliver).

Once in a while, engineers forget that when coding in C++, there's another implicit language, i.e template meta-programming, is involved. That is to say, if, as a good engineer, won't code in any language with many iterations, worst run-time algorithm, should also beware not to make the compiler coding for you fall into that situation. And yet this needs a bit of knowledge/wisdom.

This also indicates the bar of hiring qualified engineers for a C++ project is higher than using other languages, with other languages , e.g Golang, no matter how bad code is written, it just works, as for C++, it just doesn't :-P (https://en.wikipedia.org/wiki/Heisenbug bombs)

Follow up:
http://www.elbeno.com/blog/?p=1598

Jun 4, 2018

[C++][software engineering][design] How should one write a full-scale program in C++?

Just put it here as a reminder.

https://www.reddit.com/r/cpp/comments/8o8q1e/how_should_one_write_a_fullscale_program_in_c/

quote:

Most of us just make it up as we go along dude.

Its a continuous process. The fundamental issue is the technical debt as you go along. Never be afraid to refactor and push back against manager types who hate allocating time for "refactor". The growth is always organic but you have to maintain a balance between navel gazing and actually executing the idea. Tools of the trade are source control, a decent build system, Google and an evolving thought process which is atleast 2 steps ahead of the code you are writing. Don't get lost in cutting edge C++ features; Good software engineering has nothing to do with the latest compiler tricks or reading through C++ standard proposals.
[note] Nevertheless, it's still important to catch up, why? Because there are design ideas inside these proposals as well. And if we have something better, feedback to the community.

Oct 11, 2016

[cppcon 2016] CppCon 2016: Chandler Carruth “Garbage In, Garbage Out: Arguing about Undefined Behavior..."



reference:

Narrow contract:
  • Checkable (probabilistically) at runtime.
  • Provide significant value: bug finding, simplification, and/or optimization.
  • Easily explained and taught to programmers.
  • Not widely violated by existing code that works correctly and as intended.
Shifting unsigned int over 32 bit is _also_ an UB.

https://google.github.io/styleguide/cppguide.html#Integer_Types
Use signed int and take advantage of signed int arithmetic UB for optimization.
Especially on 64-bit platform due to pointer is 64-bit and int is 32 bit.

Aug 15, 2016

[software engineering] Edmond Lau: "The Effective Engineer" | Talks at Google - YouTube

https://www.youtube.com/watch?v=BnIz7H5ruy0

"Sink or swim."

1. Language doesn't know.
2. No unit tests.
3. Auh, just work hard.

Work hard but no impact.

Efforts != impact.

Staff engineer (e.g Jeffrey Dean) @ google produce 2x impact then junior engineer.

What are the highest-leverage activities for engineers?
Leverage = (impact produced)/(Time invested)

1.
Optimized in learning.
How? 侯傑: 由深而廣.
http://vsdmars.blogspot.com/2012/08/blog-post_26.html

Own your own story.
'Learn actively, read books, side projects, attain talks.
_not_ waiting for opportunities'

Making a habit out of it.

2.
Invest in iteration speed.

How? Making your own tools!
Have I? _yes_
https://github.com/verbalsaintmars/srm_db_tool
https://github.com/verbalsaintmars/srmparserlite
Both of these srm tool become the de facto standard in our team.

How quickly can we get things done.
Investing in tool.

People who are successful most of them have their own tools
Those strong engineers spent 1/3 of their time writing tools.

If you have to do it manually more than twice, write a tool for that.

3.
Validate ideas aggressively & iteratively

What's the most scariest area of the project.
Tackle on that first.

4.
Minimize operation burden.

5.
Do the simple thing first.
http://instagram-engineering.tumblr.com/

6.
Organization complexity.

7.
What's the simplest solution to the problem.

8.
Build a great engineer culture.

Tooling, learning, productivities etc.

9.
last but not least: READING.

Jan 31, 2015

[Defensive Programming]

Reading notes of
John Lakos "Defensive Programming Done Right, Part I"
John Lakos "Defensive Programming Done Right, Part II"
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4075.pdf

Design by Contract:

1. What it does
2. What it returns
3. Essential Behavior
4. Undefined behavior unless...
5. Note that


Verification:
1. Preconditions
  RTFM read the fxcking manual
  Assert

2. Postconditions
  Component-level test drivers

3. Invariants
  Assert invatiants in the destructor


-----
4 things:
  1. component level testing
  2. peer review
  3. static analysis tool
  4. DP
-----

Why do we assert only in safe debug mode?

Put assert on postconditions? Time to confirm th test.

Must the code itself _preserve_ invariants even if one or more preconditions of a contract are violated?
NO!!!

-----

What is DP?
Redundant code that provides runtime checks to detect and report
(but not 'handle' or hide) defects in software.

DP good or bad?
Both.
add overhead, but help idenfity defects.

What are we defending against?
1. Bugs in software we use. (Not DP!)
2. Bugs we introduced. (No...)
3. Misuse by our clients. (YES!!)
-----

Fat interface / bad / not proper inheritance
Large interface / bad / not minimal/primitive
Wide contract / bad / No preconditions

------
Narrow contracts imply UB!

Assert inside the funciton. Don't just return 0/false/none

------
Should setXXX mem_fn return status?
NO!!!!

Returning status implies wide contract.

Wide contracts prevent defending against
  such errors in _any_ build mode.


Beware!! assert is expansive!!


Narror contract only check in debug mode.
Wide contract checks by every call!
------

Preconditions _always_ imply postconditions.

**
If a function cannot satisfy it's contract,
it must _not_ return normally.

------
abort() should be considered a viable alternative to
throw in virtually all cases(if exceptions are disabled)

-----
**
Good library components are exception-neutral! (via RAII)

-----
**
What should happen when the behavior is undefined?
Should what happens be part of the component-level contract?
  NO!! Otherwise it would be a behavior contract.
  Do not document UB!
  Otherwise it's not UB anymore!
-----

-----------------------------------
Implementing defensive checks.


---
What should happen if the client misuses library code??

Ask library component to warn us?

1. Document
2. Detect UB
3. Detect all UB?
4. How much CPU time should spend trying to detect misuse of library code?

a. Terminate the program
b. Thrown exception
  c. Save client work before terminate
  d. log error
  e. sent back to programming msg?


---
Plan(Part 1):
Provide 3 kinds of assert macros for library writer to use.

Assert_Opt(Expr)  < 5% CPU

Assert(Expr)  5% ~ 20% CPU

Assert_Safe(Expr) > 20%

---
Plan(Part 2)
Provide a global callback facility.

using handler = void (*)(const char* text, const char* file, int line)
// move the parameters into a single Struct type..


1. failAbort // print and terminate
2. failThrow // wrap into std::logic_error
3. failSleep // print to stderr and spin/sleep

---

e.g
void fun()
{
  auto result = library_func(); //library_func implement assert
}


---
Rule of thumb(guideline):
1. inline functions use Assert_Safe
2. Non-inline functions use Assert

---
Hard vs. Soft UB:
2 separate notion of UB.

1. Language UB (Hard UB)

2. Library UB (Soft UB)

With Library(i.e Soft) UB, (a contract violation)
nothing dire has happened yet, but it could easily lead to Language UB.

Soft UB is relative:
  . not privy to the implementation, might lead to Hard UB.
  . Contract violation can remain Soft UB.


--------
Compile macro build mode.

--------

Mixed-Mode Assertion Build

1. ABI incompatibility.
No.
  .all assertion-level modes musy be ABI compatible.
  .Only permitted side-effect is to invoke the failure handler.

2. Violating the ODR. Yes
-----------

c++11 noexcept: and C++14 follow this standard.
1. Use only for move constructors and move assignment
2. Use whereever there is a wide contract, and the operation must never throw.
3. Use whereever the operation must never throw in contract.

Aug 10, 2014

[C++11][note] C++11 Library Design

Excerpt from Aerix Consulting

Study notes:

1. Function Interface Design

Is my function? :
  • easy to call correctly?
  • hard to call incorrectly?
  • efficient to call?
  • with minimal copying?
  • with minimal aliasing?
  • without unnecessary resource allocation?
  • easily composable with other functions?
  • usable in higher-order constructs?
What’s the best way of getting data into and out of a function?
  • Input Argument Categories
    • Read-only: value is only ever read from, never modified or stored
      • const l-ref (except small ones)
    • Sink: value is consumed, stored, or mutated locally
      • Goal: Avoid unnecessary copies, allow temporaries to be moved in.
        • const l-ref and copy it into local variable
        • r-ref. Take the r-value into function.
      • What if the function takes more than 1 sink argument?
        • Take sink arguments by value.
          Since if passing in r-value, the r-value will be created as the l-value argument.
          No extra copy constructor call needed. (extra reference: WANT SPEED? DON’T (ALWAYS) PASS BY VALUE.)
    • Eric Niebler : Out Parameters, Move Semantics, and Stateful Algorithms
    • Encapsulate an algorithm’s state in an object that implements the algorithm.
2. Class Design
Can my type be…?:
  • …copied and assigned?
  • …efficiently passed and returned?
  • …efficiently inserted into a vector?
  • …sorted?
  • …used in a map? An unordered_map?
  • …iterated over (if it’s a collection)?
  • …streamed?
  • …used to declare global constants?

  • Regular Types
    • Reference:
    • Basically, int-like types.
    • Copyable, default constructable, assignable, equality-comparable, swappable, order-able
    • They let us reason mathematically
    • The STL containers and algorithms assume regularity in many places
    • Make your types regular (if possible)
    • **Make your types’ move operations noexcept (if possible)
    • Q: Is my type Regular?
      A: Check it at compile time!
template<typename T>
struct is_regular
: std::integral_constant< bool,
std::is_default_constructible<T>::value &&
std::is_copy_constructible<T>::value &&
std::is_move_constructible<T>::value &&
std::is_copy_assignable<T>::value &&
std::is_move_assignable<T>::value >
{};
struct T {};
static_assert(is_regular<T>::value, "huh?");


Movable Types:
  • The moved-from state must be part of a class’s invariant.
  • If above doesn’t make sense, the type isn’t movable.
  • Every movable type must have a cheap(er)-to-construct, valid default state.


3. Module
Think:
  • In C++11, what support is there for…
  • … enforcing acyclic, hierarchical physical component dependencies? 
  • … decomposing large components into smaller ones? 
  • … achieving extensibility of components? 
  • … versioning (source & binary) components?
New library version with interface-breaking changes:


  • Put all interface elements in a versioning namespace from day one
  • Make the current version namespace inline

Name Hijacking: Unintentional ADL finds the wrong overload:
  • template arguments are participating ADL.

ADL:
If the class is a class template instantiation, then the types of the template type arguments and the classes and namespaces in which the template template arguments are declared are also included.

Solution:
1. Use a non-inline ADL-blocking namespace
2. Use global function objects instead of free functions(i.e: Global function object, aka. functor)




C++14 Variable Templates:
cpp14_variable_templates

template<typename T> struct lexical_cast_fn 
{ 
template<typename U> T operator()(U const &u) const 
{ //... } 
};

template<typename T> constexpr lexical_cast_fn<T> lexical_cast{};

int main()
{ 
lexical_cast<int>("42");
}


Ode To Function Objects:
  • They are never found by ADL!
  • If phase 1 lookup finds an object instead of a function, ADL is disabled!
  • They are first class objects
  • Easy to bind
  • Easy to pass to higher-order functions like std::accumulate

Guideline:
  • Put type definitions in an ADL-blocking (non-inline!)
    namespaces and export then with a using declaration, or…
  • Prefer global constexpr function objects over named free functions (except for documented customization points)

Aug 4, 2014

[C++11] Guideline for lots of things XD

Reference: Beware of C++ - Nicolai Josuttus


Guideline for explicit constructor
  • The default constructor should never be explicit
    • If all arguments of an explicit constructor have default values, declare the default constructor separately
  • An initializer list constructor should never be explicit
    • otherwise, empty initializer list could match to default argument constructor:
      Constructor(int=0){};
  • Any other constructor should be explicit,
    if
    • parameters affect behavior instead of core content
  • Shouldn't the default constructor always be its own beast?
Guidelines for constexpr:

  • constexpr is not for optimization. The compilers can inline well already.
  • use constexpr when guaranteed static initialization is important
    e.g
    the construction of global atomics really cannot be deferred to run time.
  • use constexpr when you anticipate using the results to define array sizes or appear within template non-type arguments
  • "Making everything possible constexpr" is borderline insane. It leads to unnecessarily increased compile times, potential code bloat, and wishes to overload on constexpr so that we can select different algorithms for compile time and run time.
  • by all means "be generous", but use constexpr only when there is a potential need for guaranteed compile-time evaluation.
  • beneficial uses of constexpr on non-trivial computations aren't always obvious from past experience.

Guidelines for template parameters:

  • If knowing the object is always cheap to copy then pass by value.
  • If it might not be cheap to copy, making a choice:
    • if the expected type is likely to be an r-value and is moveable, then call by value so that the caller passes temporaries or uses move.
    • if it's not cheap to copy and not moveable, then still take by value and let the caller use std::ref()
    • otherwise use const l-value reference
      • think about whether and where to decay
  • If returning s.t in the argument, use a non-const l-value reference
  • If having to pass move semantics into other parts of the called function, declare as universal reference and forward<>
    • think about whether and where to decay

Mar 7, 2012

[software engineering] books read


Design Patterns: Elements of Reusable Object-Oriented Software

I've got this book long time ago, even before I am really into C++.
This book is based on the C++ language.
There are a lot of techniques are based on C++ language features, now when I re-read it, it makes more sense.
It's not a entry level book for DP, and I believe basing on which language you use, you should read a DP book written in that language. Although the general concepts are the same, but implementation details are totally different.

The Mythical Man-Month: Essays on Software Engineering, Anniversary Edition