139 karma · joined January 7, 2020
Example with 3 "values". This is the base definition from which all the tables and enums are produced.
#define FOREACH_LAN(LAN)\
LAN(GA, IE, C_ANSI ) /**< Irish Gaelic : 0 */ \
LAN(DE, DE, C_ANSI ) /**< German : 1 */ \
LAN(DA, DA, C_ANSI ) /**< Danish : 2 */ \
LAN(EL, EL, C_GREEK ) /**< Greek : 3 */ \
LAN(EN, GB, C_ANSI ) /**< English : 4 */ \
LAN(ES, ES, C_ANSI ) /**< Spanish : 5 */
etc.
Let's define an enum indexing with these languages #define GENERATE_LANIDX(lan,country,codepage) LANIDX_ ## lan,
typedef enum {
LANIDX_UNDEFINED = -1, //
FOREACH_LAN(GENERATE_LANIDX)
LANIDX_MAX // Automatically get the upper bound
} LANIDX_TYPE;
This is equivalent to typedef enum {
LANIDX_UNDEFINED = -1, //
LANIDX_GA,
LANIDX_DE,
LANIDX_DA,
LANIDX_EL,
LANIDX_EN,
LANIDX_ES,
LANIDX_MAX // Automatically get the upper bound
} LANIDX_TYPE;
but I didn't need to repeat all the language codes, the macro did it for us.Now in the module I can define tables, also without needing to repeat the codes
const char *LanIdx2LanTable[] = {
#define GENERATE_LANIDDX_2_LAN(lan,country,codepage) [1+ LANIDX_ ## lan]={#lan},
FOREACH_LAN(GENERATE_LANIDDX_2_LAN)
};
const char *LanIdx2CodePageTable[] = {
#define GENERATE_LANCP(lan,country,codepage) [1+LANIDX_ ## lan]=codepage,
FOREACH_LAN(GENERATE_LANCP)
}; typedef bool foo_fn(int, char);
This declares a type of a function. foo_fn foo1, foo2, foo3, foo4;
This declares 4 functions of the same type. It's equivalent to bool foo1(int, char);
bool foo2(int, char);
bool foo3(int, char);
bool foo4(int, char);
Unfortunately the type can not be used at function definition but that is not where function are interesting. They are neat for function pointers especially those that require casting. void function_taking_foo(int, foo_fn *);
function_taking_foo(1, foo1); // no cast necessary as type is identical and even
instead of function_taking_foo(1, (bool(*)(int,char))foo1);
When you have a lot function pointers it is incredibly more readable than the usual syntax.It was the Falcon who had incredible sound capacity with its matrix channel mixer and DSP.
In feeble languages with simpleton typesystem may be, but in highly generic templated language like D it is not the case. The type is not dense at all.
What's funny is that in general people complain that compiler errors in D are unreadable. You know why they are unreadable?
Because they print out the types of the functions in which the error occurs and that is nothing more than word salad for generic functions.
Types with hundreds of characters are very common.
The issue arises only with generic heavily templated functions. Nothing in D forbids you to write your programs with all types explicitly written down. That's btw how I mostly write my code.
The language expresses its types just fine (it's in the mangled name in the object file). The issue is that there is no point in the human readable form of these types.
It is not a problem, the compiler copes with it. The problem comes from the fact that such a type is absolutely not interesting to know how it is written. The unmangled type is unreadable.
Template declarations in D take 2 parameter lists. The first is the template parameters the second the runtime parameter: in auto whatDoesItReturn(int i)() { static if (i == 0) { return int.init; } else { return string.init; } }
we have (int i) as template parameter and () as an empty runtime parameter. In C++ syntax whatDoesItReturn<int i>()
at instanciation the syntax is different:
whatDoesItReturn!0() will instantiate a function returning an int
whatDoesItReturn!42() will instantiate a function returning a string.
&"Yes\0No"[!x>>2]; // same number of characters
to shut it up.
I use !=0 in only one context, if(strcmp(a,b) != 0) to check if two strings differ. Normally if an if() expression I don't check explicitly for 0 or not 0, except for strcmp class of functions, because of the ordered comparison of it (<0, 0, >0) , its "boolean" value is inverted, so somewhat counter intuitive if used with implicit boolean values.
is the more important imo than
git reflog
Make a git log --reflog --all and you will see all the commits you made (or rebase made) in the last 3 months.
You can than rescucitate an old branch by simply putting a branch name on it with git branch newname <old sha1>
git log --reflog --all
you will see with this magical command that git DOESN'T REMOVE any commit. Your old tree is still there, only normally hidden. The commit that was there before you fouled up your branch is still there.
You now only need to set your branch to the old commit. A branch is nothing else than a pointer in the tree.
You have 2 possiblities to change the commit a branch points to
1. git branch --force <branchname> SHA1 (works only if <branchname> is not the current checked-out branch. Simply checking out with the SHA1 works also as it deteches the HEAD).
2. replace the SHA1 in the text file in .git/refs/heads/<branchname> by the SHA1 where you want the branch to point to.
With that, your repo is in the same state it was before your error.