From dcbde6d163dc7777a2945fda25a77b83a95209c9 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 21 Nov 2024 18:07:30 +0100 Subject: [PATCH] Library: shorten display of unsigned types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I changed the rendering of unsigned types in diagnostic output to use the short notation, e.g. `uint` instead of `unsigned int`. This dramatically improves the legibility of verification strings. Moreover, I took the opportunigy to look through the existing page with codeing style guides to explicitly write down some conventions formed over years of usage. I did not just »make up« those light heartedly, rather these conventions are the result of a craftsman's ''attentive observation and self-reflection.'' --- doc/technical/code/codingGuidelines.txt | 54 ++++++++++++--- src/lib/format-obj.cpp | 9 ++- src/lib/integral.hpp | 6 +- src/lib/meta/util.hpp | 8 +-- src/lib/rational.hpp | 2 +- src/lib/time/timevalue.hpp | 2 +- src/lib/util-quant.hpp | 4 +- tests/15library.tests | 4 +- tests/library/iter-explorer-test.cpp | 2 +- .../meta/duck-detector-extension-test.cpp | 6 +- .../library/meta/function-signature-test.cpp | 68 +++++++++---------- tests/library/result-test.cpp | 2 +- tests/library/util-floordiv-test.cpp | 2 +- 13 files changed, 109 insertions(+), 60 deletions(-) diff --git a/doc/technical/code/codingGuidelines.txt b/doc/technical/code/codingGuidelines.txt index 5fdb01d42..a27d8ddca 100644 --- a/doc/technical/code/codingGuidelines.txt +++ b/doc/technical/code/codingGuidelines.txt @@ -1,16 +1,20 @@ Coding Guidelines ================= -:Date: Spring 2023 +:Date: Autumn 2024 :toc: -_this page summarises some style and coding guidelines for the Lumiera code base_ +.Basic Tenet +Code is written for humans. Use your judgement and common sense above all. + + Style Guide ----------- The Lumiera project uses GNU indentation style with slight adaptations. -- *no tabs* please. The typical ``semi indent'' of GNU style thus becomes 2 spaces. +- *no tabs* please. + + The typical ``semi indent'' of GNU style thus becomes 2 spaces. - maximum line length is rather around *110 characters*.footnote:[This is not a hard limit, rather a guideline -- however, you should never try to stuff several distinct topics into a single line...] @@ -20,26 +24,60 @@ The Lumiera project uses GNU indentation style with slight adaptations. * the braces for a class scope are indented by 2 spaces * the access modifiers start at this brace level, while all declarations and definitions within the class are again indented by 2 spaces + * the braces for free-standing functions however are not indented and thus placed directly + below the name of the function in the definition. This holds also for member functions + defined out-line and not within the class body. * the line breaking rules are relaxed. Definitions and statements may be written as single line, - provided that the length remains below 110 chars and the result _remains legible_. Otherwise, - we'll fall back to the default and wrap the lines. More specifically + provided that the length remains below 110 chars and the result _remains legible_. + More specifically... ** function declarations may be written in one line ** same for definitions with just a single statement in the body ** same for simple if-statements without else-branch. + ** If in doubt however, we prefer to wrap the lines. + * it is OK to write if-then-else without braces, but only as long as the structure + is uttermost clear and poses no danger for maintenance. Use your common sense on this one. + Often, a ternary operator is a viable alternative. When a `?` ... `:` becomes long and + hard to read, the alternative clause starting with `:` is placed directly below the `?` * the space between function name and opening parenthesis of the argument list is not - enforced when this doesn't make sense, esp. for argument-less functions, chained calls + enforced when this doesn't make sense, notably for argument-less functions, chained calls or constructor syntax. But in all other cases, we really value this additional space, it improves legibility. * template argument declarations are _always_ written on a separate line, above the return type declaration. This rule holds even if the rest of a definition can be written within a single line. + * the body of λ-functions is indented beyond the capture clause, i.e. it is rather + starkly offset with respect to the regular code. When capture clauses become complicated + beyond just a short list of names -- especially when including initialisers -- they are + preferably written on a separate line, directly above the argument list in parenthesis * the opening brace of namespaces is placed on the same line. Optionally, the namespace body may be indented, as long as this helps underpinning the nesting structure. But there is no need to use 3 indents on a 3 level nested namespace. One level is enough to highlight the presence of a nesting. +- the shorthand type names `uint`, `ulong`, `uchar`, `short`, `ushort`, `llong`, `ullong` + are preferred, as it is clearer to write the fundamental type in a single word. Index + variables _should always be unsigned._ Use `size_t` whenever applicable.footnote:[yet it is OK + to use `uint` in a for-loop where the limits are clear; likewise it is acceptable to use + a direct conversion signed ⟷ unsigned when the situation is simple and obvious. No need + to be pedantic on everything] + When the bit-size matters, please use explicit designations like `int32_t`, `uint64_t`. +- we prefer to highlight the _typing as distinct_ from the names and entities; notably + the `*` pointer and `&` reference modifier are attached post-fix to the type, as opposed + to sticking it to the name: write ```int* thing`'', not ```int *thing`''. Whenever types + become difficult to parse, we introduce a type alias (`using Alias =`... with an uppercase + type alias name). This is especially relevant with function pointers, where we prefer + to define a type alias for the _function signature_ first. +- in a similar vein, we distinguish between an ```const object`'' which _is really in-itself_ + unmodifiable, as opposed to _taking a const-reference,_ which is always spelled out + post-fix as `object const&`, or a constant-pointer spelled as `const*`. +- we use the textual form of the boolean operators `and`, `or` and `not`, because this + allows to make the code resemble complete meaningful sentences in human language. + We _do use_ the »line noise« variant however for all low-level bit manipulation, + `&`,`|`, `^`, sometimes also for `!` -- using those is taken as an indication of + entering the ``danger zone''... + Naming conventions ~~~~~~~~~~~~~~~~~~ Naming conventions are used to characterise the kind of element at hand and give a visual @@ -159,7 +197,7 @@ Recommendations at code level * always consider the _ownership_ and _lifecycle_ -- value objects can not have any, should ideally be immutable, and stored inline or avoid heap allocation. If in doubt, decompose and turn unclear and changing parts into a service / dependency, attached by (value) handle - * objects with reference semantics should be made _noncopyable_, while value objects should use + * objects with reference semantics should be made _non-copyable_, while value objects should use default copy semantics. Use 'lib/nocopy.hpp' to express the flavours of move only / no copy. * equality comparisons of ref objects are to be based on their identity solely, while for value objects, all properties must be included which are tangible and independently variable. @@ -175,7 +213,7 @@ Recommendations at code level + * if is something is ``just a number'' yet has some specific meaning, better use a lightweight wrapper object to tag it with semantics - * if a common pattern works involving distinct, unrelated entities, + * if a common pattern works by involving distinct, unrelated entities, then better use generic programming or even higher-kinded types, instead of forcing unrelated types to inherit from some supertype. * avoid downcasts, `void*` and switch-on-type programming; this diff --git a/src/lib/format-obj.cpp b/src/lib/format-obj.cpp index b681ba7ea..23080781b 100644 --- a/src/lib/format-obj.cpp +++ b/src/lib/format-obj.cpp @@ -207,6 +207,11 @@ apologies for that." "|lumiera::" , regex::ECMAScript | regex::optimize}; + static regex lolong {"long long" + , regex::ECMAScript | regex::optimize}; + static regex unSigned {"unsigned (\\w+)" + , regex::ECMAScript | regex::optimize}; + static regex stdString {"(__cxx11::)?basic_string, allocator\\s*>(\\s+\\B)?" , regex::ECMAScript | regex::optimize}; @@ -219,7 +224,7 @@ apologies for that." static regex uniquePtr {"unique_ptr<(\\w+), default_delete<\\1>\\s*" , regex::ECMAScript | regex::optimize}; - static regex lumieraP {"P<(\\w+), shared_ptr<\\1>\\s*" + static regex lumieraP {"P<(\\w+), shared_ptr<\\1>\\s*" , regex::ECMAScript | regex::optimize}; @@ -227,6 +232,8 @@ apologies for that." auto end = typeName.end(); end = regex_replace(pos, pos, end, commonPrefixes, ""); + end = regex_replace(pos, pos, end, lolong, "llong"); + end = regex_replace(pos, pos, end, unSigned, "u$1"); end = regex_replace(pos, pos, end, stdString, "string"); end = regex_replace(pos, pos, end, stdAllocator, "$1"); end = regex_replace(pos, pos, end, mapAllocator, "$1"); diff --git a/src/lib/integral.hpp b/src/lib/integral.hpp index 0f1df0584..c77e1cc26 100644 --- a/src/lib/integral.hpp +++ b/src/lib/integral.hpp @@ -26,8 +26,12 @@ #include /* === minimal common place === */ -using uchar = unsigned char; using uint = unsigned int; +using uchar = unsigned char; +using ulong = unsigned long int; +using llong = long long int; +using ullong = unsigned long long int; +using ushort = unsigned short int; using f128 = long double; static_assert(10 <= sizeof(f128)); diff --git a/src/lib/meta/util.hpp b/src/lib/meta/util.hpp index 1eeae75a7..13f40acf2 100644 --- a/src/lib/meta/util.hpp +++ b/src/lib/meta/util.hpp @@ -495,19 +495,19 @@ namespace util { /* === Literals for common size designations === */ inline uint -operator""_KiB (unsigned long long const siz) +operator""_KiB (ullong const siz) { return uint(siz) * 1024u; } inline uint -operator""_MiB (unsigned long long const siz) +operator""_MiB (ullong const siz) { return uint(siz) * 1024u*1024u; } -inline unsigned long long -operator""_GiB (unsigned long long const siz) +inline ullong +operator""_GiB (ullong const siz) { return siz * 1024uLL*1024uLL*1024uLL; } diff --git a/src/lib/rational.hpp b/src/lib/rational.hpp index 6fd87ad9d..a9851037a 100644 --- a/src/lib/rational.hpp +++ b/src/lib/rational.hpp @@ -162,7 +162,7 @@ namespace util { * \endcode */ inline util::Rat -operator""_r (unsigned long long num) +operator""_r (ullong num) { return util::Rat{num}; } diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index 485c425e3..5e2f380b1 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -257,7 +257,7 @@ namespace time { return *this; } - /// Support mixing with plain long int arithmetics + /// Support mixing with plain 64bit int arithmetics operator gavl_time_t() const { return t_; } /// Support for micro-tick precise time arithmetics operator FSecs() const { return FSecs{t_, TimeValue::SCALE}; } diff --git a/src/lib/util-quant.hpp b/src/lib/util-quant.hpp index 275ef3139..0324ca395 100644 --- a/src/lib/util-quant.hpp +++ b/src/lib/util-quant.hpp @@ -69,10 +69,10 @@ namespace util { }; template<> - struct IDiv + struct IDiv : lldiv_t { - IDiv (long long num, long long den) + IDiv (llong num, llong den) : lldiv_t(lldiv (num,den)) { } }; diff --git a/tests/15library.tests b/tests/15library.tests index 6a8e4c654..e7be2d664 100644 --- a/tests/15library.tests +++ b/tests/15library.tests @@ -160,8 +160,8 @@ out-lit: Digxel >--empty-- 0--(val=123)--123| out-lit: Digxel >--empty--00.000--(val=123.457)--123.457| out-lit: Digxel--empty--00--(val=42)--42| out-lit: Digxel--empty--00--(val=-5)---5| -out-lit: Digxel--empty--00--(val=12)--0C| -out-lit: Digxel--empty--00--(val=111)--6F| +out-lit: Digxel--empty--00--(val=12)--0C| +out-lit: Digxel--empty--00--(val=111)--6F| out-lit: Digxel--empty--0000--(val=-1234567890)---1234567890| return: 0 END diff --git a/tests/library/iter-explorer-test.cpp b/tests/library/iter-explorer-test.cpp index d2d89babe..07e41743a 100644 --- a/tests/library/iter-explorer-test.cpp +++ b/tests/library/iter-explorer-test.cpp @@ -694,7 +694,7 @@ namespace test{ CHECK(not ii.getRestElms()); CHECK (materialise(ii.getGroupedElms()) == "23-22-21-20-19"_expect); - CHECK ( test::showType()== "array&"_expect); + CHECK ( test::showType()== "array&"_expect); uint s = *(ii.getGroupedElms()); for ( ; ii; ++ii) diff --git a/tests/library/meta/duck-detector-extension-test.cpp b/tests/library/meta/duck-detector-extension-test.cpp index dc1861a6e..73934f8f6 100644 --- a/tests/library/meta/duck-detector-extension-test.cpp +++ b/tests/library/meta/duck-detector-extension-test.cpp @@ -53,14 +53,14 @@ namespace test{ double funny (char, char, string); void funky() const; short fuzzy (float, float); - long long fuzzy(); + llong fuzzy(); double fully; }; class Fishy { /** @note private function can never be detected */ - long long fuzzy(); + llong fuzzy(); /** @note type Fishy exposes an extension point `fun` */ friend void fun (Fishy&); @@ -94,7 +94,7 @@ namespace test{ META_DETECT_EXTENSION_POINT (fun); META_DETECT_FUNCTION (double, funny, (char, char, string)); - META_DETECT_FUNCTION (long long, fuzzy, (void)); + META_DETECT_FUNCTION (llong, fuzzy, (void)); META_DETECT_FUNCTION_NAME (funny); META_DETECT_FUNCTION_NAME (funky); META_DETECT_FUNCTION_NAME (fuzzy); diff --git a/tests/library/meta/function-signature-test.cpp b/tests/library/meta/function-signature-test.cpp index fea1b2180..95fd7dc64 100644 --- a/tests/library/meta/function-signature-test.cpp +++ b/tests/library/meta/function-signature-test.cpp @@ -133,7 +133,7 @@ namespace test { // this is how the key trick of the _Fun traits template works: // for anything "function like" we retrieve a member-pointer to the function call operator // which we then pass on to the dedicated overload for member pointers - CHECK ("int (Functor::*)(unsigned int)" == typeStr()); + CHECK ("int (Functor::*)(uint)" == typeStr()); Func f1{freeFun}; @@ -150,60 +150,60 @@ namespace test { Func f5{lambda}; - CHECK ("int (unsigned int)" == showSig (freeFun)); - CHECK ("int (unsigned int)" == showSig (&freeFun)); - CHECK ("int (unsigned int)" == showSig (Functor::staticFun)); - CHECK ("int (unsigned int)" == showSig (lambda)); - CHECK ("int (unsigned int)" == showSig (f5)); + CHECK ("int (uint)" == showSig (freeFun)); + CHECK ("int (uint)" == showSig (&freeFun)); + CHECK ("int (uint)" == showSig (Functor::staticFun)); + CHECK ("int (uint)" == showSig (lambda)); + CHECK ("int (uint)" == showSig (f5)); - CHECK ("int (unsigned int)" == showSigRef (freeFun)); - CHECK ("int (unsigned int)" == showSigRef (lambda)); - CHECK ("int (unsigned int)" == showSigRef (f5)); + CHECK ("int (uint)" == showSigRef (freeFun)); + CHECK ("int (uint)" == showSigRef (lambda)); + CHECK ("int (uint)" == showSigRef (f5)); - CHECK ("int (unsigned int)" == showSigCRef (freeFun)); - CHECK ("int (unsigned int)" == showSigCRef (lambda)); - CHECK ("int (unsigned int)" == showSigCRef (f5)); + CHECK ("int (uint)" == showSigCRef (freeFun)); + CHECK ("int (uint)" == showSigCRef (lambda)); + CHECK ("int (uint)" == showSigCRef (f5)); - CHECK ("int (unsigned int)" == showSigRRef (move(lambda))); - CHECK ("int (unsigned int)" == showSigRRef (move(f5))); + CHECK ("int (uint)" == showSigRRef (move(lambda))); + CHECK ("int (uint)" == showSigRRef (move(f5))); - CHECK ("int (unsigned int)" == showSig (move(&freeFun))); - CHECK ("int (unsigned int)" == showSig (move(lambda))); - CHECK ("int (unsigned int)" == showSig (move(f5))); + CHECK ("int (uint)" == showSig (move(&freeFun))); + CHECK ("int (uint)" == showSig (move(lambda))); + CHECK ("int (uint)" == showSig (move(f5))); Func& funRef = f1; Functor& funkyRef = funk; Func const& funCRef = f1; Functor const& funkyCRef = funk; - CHECK ("int (unsigned int)" == showSig (funRef)); - CHECK ("int (unsigned int)" == showSig (funkyRef)); - CHECK ("int (unsigned int)" == showSig (funCRef)); - CHECK ("int (unsigned int)" == showSig (funkyCRef)); + CHECK ("int (uint)" == showSig (funRef)); + CHECK ("int (uint)" == showSig (funkyRef)); + CHECK ("int (uint)" == showSig (funCRef)); + CHECK ("int (uint)" == showSig (funkyCRef)); - CHECK ("int (unsigned int)" == typeStr<_Fun::Sig >()); - CHECK ("int (unsigned int)" == typeStr<_Fun::Sig >()); - CHECK ("int (unsigned int)" == typeStr<_Fun::Sig >()); - CHECK ("int (unsigned int)" == typeStr<_Fun::Sig >()); - CHECK ("int (unsigned int)" == typeStr<_Fun::Sig >()); - CHECK ("int (unsigned int)" == typeStr<_Fun::Sig >()); - CHECK ("int (unsigned int)" == typeStr<_Fun::Sig>()); + CHECK ("int (uint)" == typeStr<_Fun::Sig >()); + CHECK ("int (uint)" == typeStr<_Fun::Sig >()); + CHECK ("int (uint)" == typeStr<_Fun::Sig >()); + CHECK ("int (uint)" == typeStr<_Fun::Sig >()); + CHECK ("int (uint)" == typeStr<_Fun::Sig >()); + CHECK ("int (uint)" == typeStr<_Fun::Sig >()); + CHECK ("int (uint)" == typeStr<_Fun::Sig>()); using Siggy = _Fun::Sig; - CHECK ("int (unsigned int)" == typeStr<_Fun::Sig >()); - CHECK ("int (unsigned int)" == typeStr<_Fun::Sig >()); - CHECK ("int (unsigned int)" == typeStr<_Fun::Sig >()); + CHECK ("int (uint)" == typeStr<_Fun::Sig >()); + CHECK ("int (uint)" == typeStr<_Fun::Sig >()); + CHECK ("int (uint)" == typeStr<_Fun::Sig >()); auto memfunP = &Functor::fun; FuncF fM{memfunP}; Func fMF{bind (fM, funk, _1)}; - CHECK ("int (unsigned int)" == typeStr<_Fun::Sig>()); - CHECK ("int (Functor&, unsigned int)" == typeStr<_Fun::Sig >()); - CHECK ("int (unsigned int)" == typeStr<_Fun::Sig >()); + CHECK ("int (uint)" == typeStr<_Fun::Sig>()); + CHECK ("int (Functor&, uint)" == typeStr<_Fun::Sig >()); + CHECK ("int (uint)" == typeStr<_Fun::Sig >()); // _Fun can be used for metaprogramming with enable_if diff --git a/tests/library/result-test.cpp b/tests/library/result-test.cpp index d63148f20..db985f4ae 100644 --- a/tests/library/result-test.cpp +++ b/tests/library/result-test.cpp @@ -113,7 +113,7 @@ namespace test{ auto breed = Result{evil, 55ll}; // an odd number... VERIFY_ERROR (STATE, breed.maybeThrow() ); - CHECK (Type(breed) == "Result"_expect); + CHECK (Type(breed) == "Result"_expect); auto dead = Result{[]{ throw 55; }}; auto deed = Result{[]{ /* :-) */ }}; diff --git a/tests/library/util-floordiv-test.cpp b/tests/library/util-floordiv-test.cpp index 052b2b8a9..8d2a47b6b 100644 --- a/tests/library/util-floordiv-test.cpp +++ b/tests/library/util-floordiv-test.cpp @@ -114,7 +114,7 @@ namespace test { verifyIntegerTypes(); verifyIntegerTypes(); verifyIntegerTypes(); - verifyIntegerTypes(); + verifyIntegerTypes(); if (!isnil (arg)) runPerformanceTest();