Main / Academy
The ClassicsMost basic, first learn examples of design patterns and algorithms
More Bite-Sized ConceptsDynamic ProgrammingProblems that require state knowledge to be stored and recalled in order to solve. Recursion is an exploratory technique that can lead to finding the faster iterative solution. Memoization and tabulation are two approaches to that iterative solution. The Fibonacci sequence is a good illustration of the principle: https://www.geeksforgeeks.org/overlapping-subproblems-property-in-dynamic-programming-dp-1/ EnumerationsEnums actually do declare a type. Before enums were introduced in the language, C programmers used macros: #define STOP 0 #define CAUTION 1 #define GO 2 A better, cleaner approach in modern C/C++ is to use an enum instead: enum traffic_light_states { STOP, CAUTION, GO }; // In both of the below cases, "day" is // defined as the variable of type week. enum week{Mon, Tue, Wed}; enum week day; // Or enum week{Mon, Tue, Wed}day; Using a "typedef" just simplifies declaring a variable of this type. In other words typedef is just an alias command: typedef enum { STOP, CAUTION, GO } traffic_light_states_t ; Another cool way to use enums is to auto-generate them and use them in a map with other values. #include <stdio.h> #define mklist(f) \ f(MO, MONDAY) f(TU, TUESDAY) f(WE, WEDNESDAY) #define f_enum(tag,day) day, #define f_arr(tag,day) {#tag, day}, enum weekdays { mklist(f_enum) WD_NUM }; struct { char * str; enum weekdays wd; } wdarr[] = { mklist(f_arr) }; int main() { int i; for (i=0; i < sizeof(wdarr)/sizeof(wdarr[0]); i++) { printf("%s, %d\n", wdarr[i].str, wdarr[i].wd); } } Or, here's another example: #define mk_cmd_list(f) \ f(" ",CMD_INVALID) \ f("AP",CMD_ANTPOINT) \ f("OV",CMD_OPTVOLTAGE) \ f("OF",CMD_OPTFOM) \ f("RF",CMD_RXFOM) \ f("LH",CMD_LATHOLD) \ f("SE",CMD_SETELEM) \ f("XE",CMD_CLEARELEM) \ f("XA",CMD_CLEARALL) \ f("UA",CMD_UPDATE_ALL) #define f_cmd_enum(tag,cmd) cmd, #define f_cmd_map(tag,cmd) {tag, cmd}, typedef enum { mk_cmd_list(f_cmd_enum) NUM_CMD } command_type; In modern C++ we have the new concept of an enum class, which does NOT default to an int type like the C enum does. In fact, we can set a different existing if we like: enum class Foo { A, B, C }; enum Foo : uint16_t { A, B, C }; Flexible Array MemberC99 introduced the concept of the array of undetermined size at compile time: struct student { int stud_id; int name_len; int struct_size; char stud_name[]; }; It's typical to include a member defining the size of the FAM that follows, in a sort of length+payload communications packet model. Then you need to allocate size for the data going into this array when you create a new object from this struct definition. Assigning to Pre-initialized Structure Members in Bulk// with typedef struct { int a; int b; } foo_t; // can do this foo_t foo = {0,1}; // can't do this Foo foo; foo = {0,1}; // but can do this with C99 Foo foo; foo = (foo_t){0,1}; Incomplete Types/* this is an incomplete type declaration (a specifier with a tag but without a member list) because the structures members are not defined, i.e. foo is just a tag only; no objects can be declared because the storage requirement is unknown; useful only for declarations, specifying a pointer, or creating a typedef; overrides any struct foo in an outer scope and limited to this scope */ struct foo; // this completes the definition of bar as a type typedef struct bar bar; int main() { //foo superfoo; //error: unknown type name ‘foo’ //foo* pfoo; //error: unknown type name ‘foo’ //bar sbar; //error: storage size of ‘sbar’ isn’t known bar* sbar; // this works because bar is a type, so we can declare a pointer to it // this is a different foo due to the scope restrictions of the struct struct foo; } So what is the difference between typedef struct node and struct node functionally? For the former, you can declare with node* head but with the latter you must use struct node* head to give it a type. String and Function PairsHere's an example of a cool trick for finding and executing the right function based on the c-string name chosen, illustrating many features of C, borrowed from some Freescale ARM IPU test code: typedef struct { const char *name; int32_t(*test) (ips_dev_panel_t *); } ipu_test_t; int32_t ips_display_test(ips_dev_panel_t * panel); int32_t ips_csc_test(ips_dev_panel_t * panel); int32_t ips_combiner_test(ips_dev_panel_t * panel); int32_t ips_rotate_test(ips_dev_panel_t * panel); int32_t ips_resize_test(ips_dev_panel_t * panel); static ipu_test_t ipu_tests[] = { {"IPU SINGLE DISPLAY TEST", ips_display_test}, {"IPU COMBINE DISPLAY TEST", ips_combiner_test}, {"IPU ROTATE TEST", ips_rotate_test}, {"IPU RESIZE TEST", ips_resize_test}, {"IPU CSC TEST", ips_csc_test}, }; //here, print out each test option and get the selection into int i if ((i >= 0) && (i < test_num)) { printf("\n"); retv = ipu_tests[i].test(panel); if (retv == TRUE) printf("\n%s test PASSED.\n\n", ipu_tests[i].name); else printf("\n%s test FAILED.\n\n", ipu_tests[i].name); Properly Rounding Int to Float CastThe default is to always round integers down, so if you want to actually get the closest integer to a float value you need to add 0.5 to the value before case. Make good use of parentheses. //incorrect, casts first int trueDI = (int) fpDI + 0.5; //correct, math first int trueDI = (int) (fpDI + 0.5); Turning Command-Line Options Into CodeUsing an opt_table construct, we create variables to branch on. This is from the opt module command line parser tool. /* OPT_WITHOUT_ARG() - macro for initializing an opt_table entry (without arg) * @names: the names of the option eg. "--foo", "-f" or "--foo|-f|--foobar". * @cb: the callback when the option is found. * @arg: the argument to hand to @cb. * @desc: the description for opt_usage(), or opt_hidden. */ #define OPT_WITHOUT_ARG(names, cb, arg, desc) \ { (names), OPT_CB_NOARG((cb), (arg)), { (arg) }, (desc) } /* OPT_WITH_ARG() - macro for initializing long and short option (with arg) * @names: the option names eg. "--foo=<arg>", "-f" or "-f|--foo <arg>". * @cb: the callback when the option is found (along with <arg>). * @show: the callback to print the value in get_usage (or NULL) * @arg: the argument to hand to @cb and @show * @desc: the description for opt_usage(), or opt_hidden. */ #define OPT_WITH_ARG(name, cb, show, arg, desc) \ { (name), OPT_CB_ARG((cb), (show), (arg)), { (arg) }, (desc) } /* Resolves to the four parameters for arg callbacks. */ #define OPT_CB_ARG(cb, show, arg) \ OPT_HASARG, NULL, \ typesafe_cb_cast3(char *(*)(const char *,void *), \ char *(*)(const char *, typeof(*(arg))*), \ char *(*)(const char *, const typeof(*(arg))*), \ char *(*)(const char *, const void *), \ (cb)), \ typesafe_cb_cast(void (*)(char buf[], const void *), \ void (*)(char buf[], const typeof(*(arg))*), (show)) #define OPT_CB_WITHARG(cb, show, arg) \ OPT_PROCESSARG, NULL, \ typesafe_cb_cast3(char *(*)(const char *,void *), \ char *(*)(const char *, typeof(*(arg))*), \ char *(*)(const char *, const typeof(*(arg))*), \ char *(*)(const char *, const void *), \ (cb)), \ typesafe_cb_cast(void (*)(char buf[], const void *), \ void (*)(char buf[], const typeof(*(arg))*), (show)) char *opt_set_bool(bool *b) { *b = true; return NULL; } /* Set a char *. */ char *opt_set_charp(const char *arg, char **p) { *p = (char *)arg; return NULL; } /* These options are available from config file or commandline */ static struct opt_table opt_config_table[] = { OPT_WITH_ARG("--version-file", opt_set_charp, NULL, opt_hidden, "Set version file"), OPT_WITH_ARG("--api-allow", opt_set_charp, NULL, &opt_api_allow, "Allow API access"), OPT_WITHOUT_ARG("--logwork", opt_set_bool, &opt_logwork, "Allow log work"), OPT_ENDTABLE }; Creating a struct error libraryIn C: struct WSAERRORS { int id; char *code; } WSAErrors[] = { { 0, "No error" }, { WSAEINTR, "Interrupted system call" }, ... }; An example of declaring a struct and array of said struct, populated. Portable C++ variation on this: struct TCPError { enum codes { NONE=0, ERRSOCK, ERRCONN, ERRIOCTL, ERRUNKNOWN, NUMERRCODES } errcode; const char* errmsg[NUMERRCODES] = { "no error", "unable to allocate socket", "unable to connect to TCP server", "unable to set socket to non-blocking", "unknown error code" }; TCPError() { errcode = NONE; } const char* what() const { // in bounds check if (errcode < 0 || errcode > ERRUNKNOWN) return errmsg[ERRUNKNOWN]; return errmsg[errcode]; } }; Exposing the Wiles of the Volatile KeywordCode with: volatile int bar = 5; 0x80003900: 3B 50 00 F0 MOV d15,0x5 0x80003904: 59 AF 00 00 ST.W [a10]0x0,d15 bar = 9; 0x80003914: 3B 90 00 F0 MOV d15,0x9 0x80003918: 59 AF 00 00 ST.W [a10]0x0,d15 if (bar == 9) 0x8000391C: 19 A0 00 00 LD.W d0,[a10]0x0 0x80003920: 3B 90 00 F0 MOV d15,0x9 0x80003924: 5F 0F 06 80 JNE d15,d0,0x80003930 foo = 7; 0x80003928: 3B 70 00 F0 MOV d15,0x7 0x8000392C: A5 1F 00 00 ST.W 0x10000000,d15 Code without: int bar = 5; bar = 9; if (bar == 9) foo = 7; 0x8000390A: 3B 70 00 F0 MOV d15,0x7 0x8000390E: A5 1F 00 00 ST.W 0x10000000,d15 Note that it doesn't even bother to use a register for bar. How To Do a Variadic Functionsweet printf wrapper with variable format args void fooprint(const char * __restrict format, ...); void fooprint(const char * __restrict format, ...) { va_list argslist; va_start(argslist, format); char str1[1024]; vsnprintf(str1, sizeof(str1), format, argslist); str1[sizeof(str1)-1] = 0; char str2[2048]; // the MS version of this function has an underscore in front #if defined WIN32 || defined _WIN32 _snprintf(str2, sizeof(str2), "%s: %s\n", app_name, str1); #else snprintf(str2, sizeof(str2), "%s: %s\n", app_name, str1); #endif str2[sizeof(str2)-1] = 0; <CALL SYSTEM DEBUG PRINT HERE>(str2); va_end(argslist); } Variadic debug print template you can put in your modules /*! * \brief module-local printf-style debug output * * Note that this routine may be called at interrupt time so it * must not block, allocate memory, etc.. We know that the bsp's * debug_printf() and friends handle this correctly, so we just * delegate to them. * * This is localized in a static inline function to allow single-point * enable/disable for the module. */ static inline int DEBUG(char const *fmt, ...) { int n = 0; if (ENABLE_DEBUG_MESSAGES) { // the bsp's debug_vprintf() routine is safe to call at any IPL va_list valist; va_start(valist,fmt); n = bsp::debug_vprintf(fmt,valist); va_end(valist); } return n; } Struct declarationsThis works fine: typedef struct { unsigned int a; struct { unsigned int b[8]; unsigned int c[8]; } foobar; unsigned int d; } withst; typedef struct { unsigned int a; unsigned int b[8]; unsigned int c[8]; unsigned int d; } nost; const withst johnson = { 0xCE86AA8F, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, 0x43211234 }; const withst douglas = {0}; const nost tudor = { 0xCE86AA8F, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, 0x43211234 }; But this will give you compiler warnings about "excess elements": const nost johnson = { 0xCE86AA8F, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, 0x43211234 }; Fixed-width Integer TypesSee this write-up about the additions to C99 standard: https://barrgroup.com/Embedded-Systems/How-To/C-Fixed-Width-Integers-C99 Function Calls in while ConditionNote that this may not do what you think it does. var is (may be?) compared before it is re-assigned with the return value of the function, despite the order. // PROLLY NOT GOOD!! int var = 1; while ( var = foo() && var != 0) {} C-style "Objects"You can use structs to create a type, or class, of objects which are then instantiated one at a time. It would mainly consist of variables and methods, like so: typedef struct { SYS_CONSOLE_DEVICE consoleDevice; DRV_IO_INTENT intent; char (*sysConsoleReadC) (int fd); ssize_t (*sysConsoleRead) (int fd, void *buf, size_t count); ssize_t (*sysConsoleWrite) (int fd, const void *buf, size_t count); void (*sysConsoleRegisterCallback) (consoleCallbackFunction cbFunc, SYS_CONSOLE_EVENT event); void (*sysConsoleTasks) (SYS_MODULE_OBJ object); SYS_CONSOLE_STATUS (*sysConsoleStatus) (void); void (*sysConsoleFlush) (void); } SYS_CONSOLE_DEV_DESC; Note that these are function pointers and declarations, not function definitions, which you cannot put inside a struct in C (yes in C++). When the object is created, then you define all the methods it needs and give the pointers somewhere to go. Modern C++ Constructsstd::string lvalue and rvalueCan't do this: static constexpr auto foo = "foostring"; myfunc(std::string& bar) { } { myfunc(foo); } That's because the static constexpr is just an rvalue (temporary) and it's just as if you had passed the parameter "foostring" instead of foo in the myfunc() call. Remove the & and copy the entire string or declare a string type for real instead of a constexpr. Using std::threadHere is a good example of creating a thread class to use it: https://rafalcieslak.wordpress.com/2014/05/16/c11-stdthreads-managed-by-a-designated-class/ //basically std::thread foothread(thread_task_function_name); foothread.join() Using std::generateYou can give the std::generate call a Generator, which is just a function name that provides a return value to assign to each element in a range. uint8_t incfunc() { static uint8_t val = 0; return val++; } void make_fake_data(uint8_t* buf, size_t size) { std::generate(&buf[0], &buf[size-1], incfunc); } Constructors RevampedIt appears C++11 introduced a lot of built-in copy and move constructors to classes in the standard libraries, like ifstream. So you don't have to create your own copy and move constructors anymore, as we used to do. initialization (1) explicit istream (streambuf* sb); copy (2) istream& (const istream&) = delete; move (3) protected: istream& (istream&& x); Note that you'll get a linker error if you declare a constructor, but don't define it. class Foo { public: Foo(); //if you do this, you must provide a definition as well }; Ye Olde String Copy ConundrumThe std::string implementations are generally optimized for short strings anyway, which are most common, and does dynamic allocation if needed for longer strings. But it only does this when longer strings come in - once the longest string during the program life comes through, it won't need to allocate any more memory on the heap. It's as simple as this: void set_name(const std:string& name) {m_name = name;} void set_name(std:string&& name) {m_name = std::move(name);} The second is the overloaded function to account for && parameters. TemplatesA template is a way to take a single generic class or function and auto-generate versions of it to handle any given data type as parameter. The new versions are created at compile time, but only when the instruction to do so is invoked. template <class foo> class bar { typedef foo sometype; sometype name; sometype address; } Now you can invoke this with bar<intvariablename> barobject and it would take foo as a placeholder and generate a class that uses an integer as the type for the sometype vars it creates. Here's a way you can do templates for functions with different data types as arguments: template <> void LowpassFilter<float>::update_filter(float value) { //code } template <> void LowpassFilter<double>::update_filter(double value) { //code } Function SignatureJust like the return type, exception specification is part of function type, but NOT part of the function signature. The signature is comprised of the function name and arguments list. Keep this in mind when overloading a function. Timers in C++Here is a simple example of what's a convoluted API for the types in std::chrono: https://gist.github.com/mcleary/b0bf4fa88830ff7c882d Modified implementation in brief here: #ifndef _MSG_TIMER_HPP #define _MSG_TIMER_HPP #include <chrono> #define AUTOGEN_MSG_TIME_S 5.0 /* @brief Helper class for generating fake messages on a schedule */ class MsgTimer { private: std::chrono::steady_clock::time_point m_t1; public: MsgTimer() { using namespace std::chrono; m_t1 = steady_clock::now(); } ~MsgTimer() { } int get_new_msg_type() { using namespace std::chrono; steady_clock::time_point t2 = steady_clock::now(); double seconds_passed = duration_cast<seconds>(t2 - m_t1).count(); if ( seconds_passed >= AUTOGEN_MSG_TIME_S ) { m_t1 = steady_clock::now(); return msg_class_get(); } return EthFrame::MSG_CLASS_NONE; } int msg_class_get() { // alternates between message types // return x } }; #endif //_MSG_TIMER_HPP Vector and Array ConversionsWhen you want to say, copy elements from an array to a vector you can use the new std::copy function template: std::copy(foo_array.begin(), foo_array.end(), back_inserter(bar_vector)); This gets iterators (de-reference-able pointers) at the front and back of the array as the source, and then one for the last element of the vector as the destination. Note that you must use back_inserter instead of end() because end points beyond the last element. Vector of StructsHere's a common design pattern to store something you are creating and referencing. class Foo { private: struct FooBar { double start; double end; bool operator<(FooBar const &x) const noexcept { return start < x.start; } }; std::vector<FooBar> m_foobar; //!< sorted, non-overlapping collection of foobars public: FooBar() = default; FooBar(string const &cfg_filename); bool contains(double freq); // returns true if the frequency is in the foobar }; New Enum StuffYou can create a "trivial class" with an enum now and specify a type/size for the members. enum class classname : uint16_t { thing1 = 0; }; uint16_t foo = classname::thing1; or instead use a simpler namecspace option for enums (gives scope): enum spacenamefoo { thing1=0; }; int bar = spacenamefoo::thing1; autoThis is used in place of a specific type identifier; the compiler deduces the type. int i = 42; int j{42}; auto x = 42; auto y{42}; The assembly generated for the above four lines is the same. Something to consider when using auto for a new object you create dynamically. fooptr = new Foo(); auto f = *fooptr; I think what happens here is that f is trying to create a new instance of the class, a copy of the one created on the line before. This does not work and results in all kinds of compiler errors referring to using "deleted function". This is because a constructor is deleted implicitly. How is a function deleted? It's a new C++ feature, defining a function void foo() = delete;. This stops the compile if the coder tries to use the function. The most usual cause for this kind of error is after you added some custom constructor to a class - as result the compiler ceases creating the default constructor, and if an instance of the class is ever created through the default constructor, this error appears. You can add the default constructor if you want, so it's no longer implicitly deleted. This works because f is a reference to the same object, and no new instance is created. fooptr = new Foo(); auto& f = *fooptr; Another place you'll run into this problem is when trying to iterate over a vector of unique_ptr. // this is fine for unique_ptr std::vector<std::unique_ptr<Card>> cardstack; for (auto& c : cardstack) {} // this is fine for regular pointers vector<Card*> cardstack; for (auto c : cardstack) {} // but you can't do this std::vector<std::unique_ptr<Card>> cardstack; for (auto c : cardstack) {} Initializer ListsIn C++11, you can init variables with values using curly brackets. This is mostly useful for a list of values such as in creating an array. Here's a trivial example: int i = 42; int j{42}; The assembly generated for the above two lines is the same. Unique Pointer UsageCard allcards[CARD_SET_SIZE]; // this will work auto get_all_cards() { return std::unique_ptr<Card>(&allcards[0]); } //can't do it this way because you can't convert a regular pointer to unique_ptr std::unique_ptr<Card> get_all_cards() { return &allcards[0]; } Reference vs PointerThink of a variable name as a label attached to the variable's location in memory. You can then think of a reference as a second label attached to that memory location. Therefore, you can access the contents of the variable through either the original variable name or the reference.
Code examples: int i = 42; int& j = i; cout << "Value of i : " << i << endl; cout << "Value of i reference j : " << j << endl; Syntactically, use a reference just like you would a variable name and NOT like a pointer. References are always const, in the sense that you can never reseat a reference to make it refer to a different object. Never. With or without a const. Don't attach const qualifier to a reference declaration. However, you can use const for the data that is referenced. For example: void foofunc(Foo const &f); declares a function that uses f (a reference to an object of type Foo) but does not change anything inside that object. Copy Elision (Return Value Optimization)In modern C++, they have introduced a way to allow return of locals of some data types, like vectors: std::vector<int> func() { std::vector<int> foo; //... populate the vector ... return foo; } std::vector<int> bar = func(); The object is moved upon return. ExceptionsIn some Windows application code, you may see throw std::exception("some error text") but the internet will tell you there is no constructor for an exception with this signature. In fact, g++ will choke on it. It appears to be an implementation-specific issue with certain compilers allowing this. For example, I saw this work fine in a Visual Studio project. See this link: https://comp.lang.cpp.moderated.narkive.com/o4sAcKX4/why-it-is-wrong-to-say-throw-std-exception-error-msg std::exception should mostly be treated as an abstract class, as in a base class for the exception hierarchy. The constructor of the exception class does not take any arguments according to C++ standard. Microsoft C++ has a constructor taking arguments in the exception class, but this is not standard. The runtime_error class has a constructor taking arguments (char*) on both platforms, Windows and Linux. To be portable, better use runtime_error. Example 1 - there is no constructor for the exception class that accepts a string or const char* arg in the standard, but some implementations assign one (like MS VS) even though it?s an abstract class. g++ won't compile with this. I guess people who only do Windows apps say they have a habit of using this all the time. try { foo(); } catch (std::exception e) { cout << "exception: " << e.what() << "\n"; } void foo() { throw std::exception("we have a problem"); } Example 2 - this is portable. Note also the change to the catch argument reference presumably because of the derived class, or it won?t get the string message. try { foo(); } catch (std::exception& e) { cout << "exception: " << e.what() << "\n"; } void foo() { throw std::runtime_error("we have a problem"); } Exception typesNote that there are different types of exceptions. If you throw one type, but don't specify that type in your catch block, you won't catch it. There is a way to catch everything though. catch (std::runtime_error& e) {} //Only catch runtime_errors catch (const std::exception& e) {} //Catch everything Using a stream for reading from memory rather than a fileC++11 Most examples of using the istream class (fstream and stringstream) show picking a file to open as you build the object. It's less common to find people using these classes to read from memory, like a character buffer, but it can be done. It turns out that using an fstream is problematic though, and I had better success with stringstream. stringstream mStream; ... string bufstr((char*) buf, bufsize); mStream.str(bufstr); //start reading the mStream contents here In any case, you must be careful if you are trying to debug the streampos indicator. // this is broken and gives you zeroes for both values printf("pos: %d, val: %d\n", mStream.tellg(), mStream.get() ); Does not work for peek or rdbuf()->sbumpc() either. Somehow the tellg() messes with the printf. If you remove the tellg, then the get() and others will function properly. |