Showing posts with label cpp11_constexpr. Show all posts
Showing posts with label cpp11_constexpr. Show all posts

Oct 2, 2017

[c++][cppcon 2017] constexpr all the things

constexpr all the things

Reference:
cppcon2017 constexpr all the things
constexpr specifier
Constant expressions
std::variant

digress:
User-defined literals (From c++11 faq)

A literal operator can request to get its (preceding) literal passed ``cooked''
(with the value it would have had if the new suffix hadn't been defined) or ``uncooked'' (as a string).
constexpr complex<double> operator "" i(long double d) // imaginary literal
 {
  return {0,d}; // complex is a literal type
 }
 
std::string operator""s (const char* p, size_t n) // std::string literal
 {
  return string(p,n); // requires free store allocation
 }

To get an ``uncooked'' string, simply request a single const char* argument:
Bignum operator"" x(const char* p)
{
 return Bignum(p);
}

void f(Bignum);
f(1234567890123456789012345678901234567890x);


There are four kinds of literals that can be suffixed to make a user-defined literal
  • integer literal
    accepted by a literal operator taking a single unsigned long long or const char* argument.
  • floating point literal
    accepted by a literal operator taking a single long double or const char* argument.
  • string literal
    accepted by a literal operator taking a pair of (const char*, size_t) arguments.
  • character literal
    accepted by a literal operator taking a single char argument.
Suffixes will tend to be short (e.g. s for string, i for imaginary, m for meter, and x for extended),
so different uses could easily clash. Use namespaces to prevent clashes:
namespace Numerics { 
  // ...
  class Bignum { /* ... */ }; 
  namespace literals { 
   operator"" X(char const*); 
  } 
 } 

 using namespace Numerics::literals; 


Benefit of constexpr
  • Runtime efficiency
  • Clearer code, fewer magic numbers
  • Less cross-platform pain

Requirements for compile time types
  • constexpr constructor
  • std::is_trivially_destructible
constexpr allocator
template <class T, size_t Size>
struct ConstexprAllocator {
    typedef T value_type;
    consstexpr ConstexprAllocator(/*ctor args*/);
    template <class U>
    constexpr ConstexprAllocator(const ConstexprAllocator<U>& other);
    constexpr T* allocate(std::size_t n);
    constexpr void deallocate(T* p, std::size_t n);
    std::array<std::pair<bool, value_type>, Size> data; // bool for free flag
};

Currently any type with a non-trivial destructor cannot be used in constexpr context.
Reason:
    Run-time adjusting this pointer.

Solution to the constexpr destructor problem
struct Container {
    ~Container() {
    // this proposal allows for an empty destructor to be allowed
        if constexpr(something) {
        // do something
        }
    }
};

// OR
struct Container {
    ~Container() {
    // but why not treat it like any other constexpr code?
    // allow it as long as only constexpr allowed actions
    // happen at compile time?
        if (extra_data) {
            delete [] extra_data;
        }
    }
};

Oct 4, 2016

[c++] [cppcon2016] Constant fun

enum  bm { b0 = 0x1, b1 = 0x2, b2 = 0x4 };
int b = bm::b0 | bm::b1;

//---
enum  bm { b0 = 0x1, b1 = 0x2, b2 = 0x4 };
bm b = bm::b0 | bm::b1; // bad conversion

//--
enum  bm { b0 = 0x1, b1 = 0x2, b2 = 0x4 };

bm b = bm(bm::b0 | bm::b1); //OK

//--
enum class bm { b0 = 0x1, b1 = 0x2, b2 = 0x4 };
bm b = bm(bm::b0 | bm::b1); // no such op!!

//--
enum class bm { b0 = 0x1, b1 = 0x2, b2 = 0x4 };

constexpr bm operator| (bm v0, bm v1) {
return bm(int(v0) | int(v1));
}

bm b = bm::b0 | bm::b1;

switch (b) {
case bm::b0 | bm::b1: /*...*/; // OK due to constexpr operator
}

//--


May 26, 2015

[constexpr][study note] C++ at Compile Time

C++ at Compile Time

  • constexpr function must have a throw
  • Declare unresolved extern const char*
  • Reference unresolved extern in throw

  • extern const char* compile11_bin_invoked_at_runtime;
    template lt;typename T = std::uint32_t>
    constexpr T compile11_bin(
    constexpr_txt t,
    std::size_t i = 0, // index
    std::size_t b = 0, // bit count
    T x = 0) // accumulator
    {
    return
    i >= t.size() ? x : // end recursion
    b >= std::numeric_limitslt;T>::digits ?
    throw std::overflow_error("Too many bits!") :
    t[i] == ',' ? compile11_binlt;T>(t, i+1, b, x) :
    t[i] == '0' ? compile11_binlt;T>(t, i+1, b+1, (x*2)+0) :
    t[i] == '1' ? compile11_binlt;T>(t, i+1, b+1, (x*2)+1) :
    throw std::domain_error( // Only '0', '1', and ','
    compile11_bin_invoked_at_runtime);
    }
    
    int main()
    {
    auto mask = // lt;- Not constexpr!
    compile11_binlt;std::uint8_t>("1110 0000");
    assert(mask == 0xE0);
    return 0;
    }
    

    Compile-Time Floating Point

    Oct 1, 2014

    [c++11/14][constexpr]

    The (somewhat unusual) rules of C++ dictate that when picking a member function on a temporary class object, the non-const is preferred to the const one.

    Reference:
      “constexpr” function is not “const”

    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

    Jul 23, 2014

    [C++] SFINAE trick with C++14 feature use

    Compile time introspection in C++14 using single constexpr.

    Nice use of tricks of sizeof, mentioned in Davide Di Gennaro's book:
    Advanced C++ Metaprogramming with C++14 features enabled.

    2 things to be noticed:

    1. decltype won't evaluate it's content. Which it can't take lambda expression as an argument.
    2. Lambdas can't be passed as parameters to constexpr functions. Ref: constexpr lambda functions

    The technique is made use of
    declval (std::add_rvalue_reference<T>::type)  / decltype / sizeof / template lambda / SFINAE



    Jul 9, 2014

    [C++11] constexpr with static data member array initialization

    Initialize static const multidimensional array with inferred dimensions inside class definition

    constexpr is very useful for compile time programming.

    the difference between with constexpr and without is that

    we won't have symbols while using constexpr. Everything is done

    in compile time.

    No symbols, no linkage, no address of the symbol being taking possible.

    Jun 24, 2014

    [c++] Scalar type

    Reference: What is a scalar Object in C++?


    Types in C++ are:


    • Object types: 
      • scalars
        • arithmetic (integral, float)
        • pointers: T * for any type T
        • enum
        • pointer-to-member
        • nullptr_t
      • arrays
        • T[] or T[N] for any complete, non-reference type T
      • classes (class Foo or struct Bar)
        • Trivial classes 
        • Aggregates 
        • POD classes 
        • (etc. etc.)
      • unions
        • union Zip
    • Reference types
      • T &, T && for any object or free-function type T
    • Function types
      • Free functions: R foo(Arg1, Arg2, ...)
      • Member functions: R T::foo(Arg1, Arg2, ...)
    • (Member types) [see below]
    • void

    Mar 9, 2014

    [C++11] constexpr

    constexpr definition
    LiteralType definition

    constexpr functions will be evaluated at compile time when :

    • all its arguments are constant expressions 
    • the result is used in a constant expression
    A constant expression could be 
    • a literal (like 42), 
    • a non-type template argument (like N in template<class T, size_t N> class array;), 
    • an enum element declaration (like Blue in enum Color { Red, Blue, Green };
    • another variable declared constexpr.
    They might be evaluated when all its arguments are constant expressions and the result is not used in a constant expression, but that is up to the implementation.


    • (§7.1.5/2): "constexpr functions and constexpr constructors are implicitly inline (7.1.2)."
    • constexpr member function can have declaration in the class and separate definition outside the class.
      • If any declaration of a function or function template has constexpr specifier, then all its declarations shall contain the constexpr specifier.
    constexpr int get(bool b) { return b ? 42 : throw 111; } 
    
    constexpr auto var1 = get(true); //OK 
    constexpr auto var2 = get(false); //Ill-formed 
    //--------------------------------------
    enum class TreeColor { Red, Black, Exceptionary }; 
    
    constexpr int decide(TreeColor c) { 
      switch (c) { 
      case TreeColor::Red: return 1; 
      case TreeColor::Black: return -1; 
      default: 
        throw invalid_args("Invalid TreeColor"); 
      } 
    } 
    
    virtual void foo() { 
      TreeColor c = ..; 
      int c = decide(c); // OK, may throw invalid_args 
    } 
    
    // OK, no throw expression evaluated 
    constexpr int good_col = decide(TreeColor::Red); 
    // Ill-formed, would evaluate throw expression 
    constexpr int bad_col = decide(TreeColor::Exceptionary);