Showing posts with label cpp11_inline_namespace. Show all posts
Showing posts with label cpp11_inline_namespace. Show all posts

Jan 31, 2019

[C++][note] ABI compatibility and inline namespaces - Arvid Norberg

Quick note/refresh about ABI through Arvid Norberg's talk.




ABI is about linking.
Reference:
https://vsdmars.blogspot.com/2015/09/linking-notes.html


Calling convention

Reference:
https://en.wikipedia.org/wiki/X86_calling_conventions
The history of calling conventions series - Raymond Chen
https://blogs.msdn.microsoft.com/oldnewthing/20040102-00/?p=41213
https://blogs.msdn.microsoft.com/oldnewthing/20040107-00/?p=41183
https://blogs.msdn.microsoft.com/oldnewthing/20040108-00/?p=41163
https://blogs.msdn.microsoft.com/oldnewthing/20040114-00/?p=41053
https://www.codeproject.com/Articles/1388/Calling-Conventions-Demystified
https://www.agner.org/optimize/calling_conventions.pdf
x64 calling convention
https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=vs-2017
Stack frame layout on x86-64
https://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/

- which registers to pass arguments in
- pass two 32bit arguments in 64bit registers
- split structs and pass fields in registers
- pass floats in special registers
- pass arguments in SIMD registers
- return value optimization
- how are exceptions thrown


Class layouts:

- vtable layout
- where/how we pad fields
- empty base class optimization
- std::pair (compressed_pair)
- std::string (SSO)


Across library boundaries:

- C++ version
- may affect layout
- name mangling
- calling convention
- defines
- may affect layout (e.g _GLIBCXX_USE_CXX11_ABI)
- any compiler flag alters layout or calling conventions


'#define' harms, beware of using it which breaks
the compile ABI.

In Golang, it simply asks you to recompile everything again
with same version of compiler.

In C++, we often have .so (SONAME) libraries.
This is where the trouble begins(Or solved).



Name mangling:

- Trouble in shared libraries.
- release mode library and debug mode client.
- C++98 library and C++11 client.
- Library headers is newer than library binary.



Solution:

- Auh, like Golang, recompile everything.
- Use a build system which ensures what we build is
link-compatible.
i.e
build system should give us separate library builds,
release build, debug build, etc.

If we tried to build release against debug library, build
system should fail us..



Inline namespaces come to the rescue:

Reference:
[C++] inline namespace compile time replacement trick.
https://vsdmars.blogspot.com/2016/02/c-inline-namespace-compile-time.html
C++11-FAQ-BS
http://www.stroustrup.com/C++11FAQ.html#inline-namespace

- inline namespaces let us inject information into the mangled
name(ABI), without altering the API.
i.e
for the caller always refers to the symbol inside the namespace function
regardless the namespace's internal inline namespace is.

This trick can go further since specialized temaplte can be defined
in the namespace which embeds the inline namespace's main template.

- inline namespaces affect the linker names of symbols,
While preserving the API.
From caller's perspective, the callee's name remains the same,
regardless where the name coming from different inlined namespace.



Forward declarations:

- A symbol's actual namespace is an implementation detail.
- Clients may not forward declare 3rd party symbols.
(because that symbol might be an inline namespace symbol,
which during a real linking stage, compile fails~~~)


Summary:

- Always build from the source (if there's no versioning .so, SONAME available)
- Use inline namespace for backward compatible upgrades to the ABI
(use with #define)
- Use inline namespace for ABI-safe build configurations
- Library authors, provide forward declaration headers (versioned, of course)


Feb 9, 2016

[C++] inline namespace compile time replacement trick.

Curious Namespace Trick

tl;dr
#include 

// output:
//  dmitry@t:~$ g++ func.cpp -DPLATFORM=common ; ./a.out 
//  common add
//  dmitry@t:~$ g++ func.cpp -DPLATFORM=arm ; ./a.out 
//  arm add

namespace project {
  // arm/math.h
  namespace arm {
    inline void add_() {printf("arm add\n");}  // try comment out
  }

  // math.h
  inline void add_() {
    //
    printf("common add\n");
    //
  } inline namespace PLATFORM {inline void add() {add_();}}


  inline void dot_() {
    //
    add();
    //
  } inline namespace PLATFORM {inline void dot() {dot_();}}
}

int main() {
 project::dot();
 return 1;
}

It is pretty neat, as:

  • Common/platform specific functions reside in the correct namespaces; 
  • Different platform can override different sets of functions seamlessly; 
  • Same trick works with template functions; 
  • And with template type definitions (via C++ 11 using syntax); 
  • Common implementations are still available in the original namespace to test again platform-specific code;

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)

May 19, 2014

[C++11] Inline namespace

1. Put all interface elements in a versioning namespace from day one 
2. Make the current version namespace inline
3. inline blocked ADL namespace could specialize template inside blocked ADL namespace's primary template class/func on the outside namespace.