diff options
| author | s1n <[email protected]> | 2019-08-16 03:14:37 -0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2019-08-16 03:14:37 -0700 |
| commit | febe7c42dd63c0cd6a75c323835ae766643c9ae0 (patch) | |
| tree | 449813d498f5b863ce90f053955653d566f1ad38 | |
| parent | add img (diff) | |
| download | cpp-terminology-febe7c42dd63c0cd6a75c323835ae766643c9ae0.tar.xz cpp-terminology-febe7c42dd63c0cd6a75c323835ae766643c9ae0.zip | |
large upat
| -rw-r--r-- | README.md | 133 |
1 files changed, 131 insertions, 2 deletions
@@ -1,3 +1,11 @@ +# A Simple C++ Project Structure + +One of the things I need in my new job is a bunch of blazingly fast [daemons](https://en.wikipedia.org/wiki/Daemon_(computing)) to capture market information and trade data. I prototyped them in Ruby to see what comes down the line, **but I have the need, the need for speed.** Which means I need a UNIX C or C++ framework. + +### The Project Folder Tree + +**Note:** I’m not making these to go outside my company, so the full GNU C++ standard project is overkill. Much of what follows does conform to the basics of their standard C++ project design though. + If you come from a web development background, and ended up learning C++ for some fortunate reason you must have ended up at this point at some point of time. Now if you are like most authentic coders, you must have started digging to see what experienced coders do to structure their code. Ironically even though C++ is really old and mature, the docs that are available out there are not very appealing to the current generation of coders who have been reading web framework docs which are easy to understand and follow. I don’t know why people have not taken such an initiative for C++. But truth be told, it is **not an easy language to learn.**  @@ -67,10 +75,131 @@ A point to note is there are two ways of using third party libraries in C++ — As the name suggests, code for unit testing is kept in this directory. -**5. CMakeList.txt** +**5. bin/** + +The **output executables** go here, both for the app and for any tests and spikes. + +**6. build/** + +This folder contains all object files, and is removed on a **clean.** + +**7. doc/** *or docs/** + +Any notes, like my assembly notes and configuration files, are here. I decided to create the development and production config files in here instead of in a separate **config** folder as they “document” the configuration. + +**8. spike/** + + I often write smaller classes or files to test technologies or ideas, and keep them around for future. + +**9. CMakeList.txt** This file is basically a configuration file that tells **CMake** what to do. We will talk in detail on CMakeList.txt in Part 2 of this series. Keep in mind that CMake is not a build system, but a build system generator. To understand this statement, you must know the difference between **Make** and **CMake.** We will get to this when we talk about CMake in the next article. +**10. .gitignore** + +Since I use **git** for source code control, the **.gitignore** file is: + +``` +# Ignore the build and lib dirs +build +lib/* + +# Ignore any executables +bin/* + +# Ignore Mac specific files +.DS_Store +``` + +**11. Makefile** + +I do not need the extra effort or platform independence of [autotools](https://en.wikipedia.org/wiki/GNU_Build_System). These are great for users, but suck up developer time to make them work. Instead, I opted for a simple yet flexible and generic makefile (see notes below): + +``` +# +# TODO: Move `libmongoclient.a` to /usr/local/lib so this can work on production servers +# + +CC := g++ # This is the main compiler +# CC := clang --analyze # and comment out the linker last line for sanity +SRCDIR := src +BUILDDIR := build +TARGET := bin/runner + +SRCEXT := cpp +SOURCES := $(shell find $(SRCDIR) -type f -name *.$(SRCEXT)) +OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.o)) +CFLAGS := -g # -Wall +LIB := -pthread -lmongoclient -L lib -lboost_thread-mt -lboost_filesystem-mt -lboost_system-mt +INC := -I include + +$(TARGET): $(OBJECTS) + @echo " Linking..." + @echo " $(CC) $^ -o $(TARGET) $(LIB)"; $(CC) $^ -o $(TARGET) $(LIB) + +$(BUILDDIR)/%.o: $(SRCDIR)/%.$(SRCEXT) + @mkdir -p $(BUILDDIR) + @echo " $(CC) $(CFLAGS) $(INC) -c -o $@ $<"; $(CC) $(CFLAGS) $(INC) -c -o $@ $< + +clean: + @echo " Cleaning..."; + @echo " $(RM) -r $(BUILDDIR) $(TARGET)"; $(RM) -r $(BUILDDIR) $(TARGET) + +# Tests +tester: + $(CC) $(CFLAGS) test/tester.cpp $(INC) $(LIB) -o bin/tester + +# Spikes +ticket: + $(CC) $(CFLAGS) spikes/ticket.cpp $(INC) $(LIB) -o bin/ticket + +.PHONY: clean +``` + +Notes on the Makefile: + +* The **TODO** at the top reminds me that I am using a different version of a library in development and it must be removed before deployment. + +* The **TARGET** is the main executable of the project, in this case `bin/runner`. Type **make** and this is what gets built. + +* I’m using **g++** because it’s the same on Mac OS X and on the production Linux boxes. + +* If I uncomment the **clang** line, I get a failed link as the libraries are incompatible (or comment out the last line under **$(TARGET):).** But then I get the benefit of a **clang static** analyzer run help me make my code better, well worth it. + +* I use the fewest number of compiler **CFLAGS** when developing as possible, optimization happens later. + +* The **SOURCES** list is dynamic, I don’t want to manually have to maintain this list as I program. Anything in the src folder will be included in the compile as long as it has a **SRCEXT** extension. + +* The **OBJECTS** list is also dynamic and uses a Makefile trick to build the list based on available sources. + +* The **LIB** in this case uses a local library for [MongoDB](https://www.mongodb.com/) as I am testing it, but uses the default [homebrew](https://brew.sh/) or [yum](http://yum.baseurl.org/) installed libraries for boost. I normally do not use boost, but Mongo needs it. + +* The **INC** ensures all headers in the **include** folder are accessible. + +* I like to see the commands that run, hence the multitude of **@echo's.** + +* Since there are so few of them, I manually add spikes and test builds as a new Makefile target, see the **ticket:** target for example. + +* The **.PHONY** clean is brilliant, it nukes the build folder and the main executable. It does not clean spike or test executables though. + +**Aside: Why separate the includes and the sources?** This is fundamentally not necessary for most of my expected projects as they will be stand-alone daemons. But I do expect to build a few shared libraries for these daemons, and having the **include** files separate makes them easier to deploy later on. So I may as well get into the practice of keeping them separate. + +**12. Retro Programming** + +So the retro `code-compile-run` loop looks like this: + +* Code in TextMate 2. + +* ⌘⇥ to a terminal and **make.** + +* ⌘⇥ Fix any errors in TextMate 2. + +* ⌘⇥ to a terminal and **make** again until it compiles. + +Type `bin\runner` params in terminal to run the application. **Tip: Use shell** `!` **expansion:** `!b` alone means that I have to type even less to run the last executable with the same parameter list. + +There is nothing fancy about this setup, but that is the whole point. A simple, retro environment for simple retro C++ programs. + Once you understand what CMake does and how all this comes together to generate your final binary, every thing will be crystal clear and you shall have no problem building a new project from scratch. -“You can’t trust code that you did not totally create yourself” — Ken Thompson +> “You can’t trust code that you did not totally create yourself” — Ken Thompson |