CppDepend - A Review

In my concurrency class, I use more than 60 programs. Most of the programs consist of a single source file. I'm quite curious if the static code analysis tool CppDepend can find any issues in my sources. Let me try it out.

 cppDepend

My Special Use-Case

My use-case is unique for two reasons. First, I don't expect too many issues with my examples for one reason: I give quite often concurrency classes and use these examples in my classes; therefore, I had many code reviewer. Second, my programs are most of the times not really sophisticated and quite short. They should only serve one purpose: show the particular concurrency features in isolation.

My Strategy

Due to my unique circumstances, here are my steps to get the static code analysis with CppDepend.

  1. Generate a CMake file for Visual Studio, GCC, and Clang
  2. Generate a Visual Studio Project from CMake
  3. Import the Visual Studio Project project into CppDepend
  4. Make the code analysis in CppDepend

Here we go.

1. Generate a CMake file for Visual Studio, GCC, and Clang

My customers work with Visual Studio, GCC, or Clang. To provide a way to compile all C++ sources to executables automatically, I use CMake. In sum, I have more than 300 C++ sources files in various directories. Each source file should become an executable. Writing a CMakeList.txt manually for each directory would be a boring step. Additionally, I would have to adapt it each time, if I modified the file names. Terrible!. To automate the boring stuff, I wrote a small python script generateCMakeFile.py. generateCMakeFile.py generates a CMakeList.txt file for the directory, in which I invoke it.

Here it is.

# generateCMakeFile.py

import fnmatch
import os

# A few constants
SUFFIX = "*.cpp"
OUTPUT = "CMakeLists.txt"

# The output file
outputFile = open(OUTPUT, "w")

# Return the full path to all files respecting the pattern
def getFiles(dir_, patterns="*", recursive=True):
    patterns = patterns.split()
    retValue = []
    for path, dirs, files in os.walk(dir_):
        for file_ in files:
            for pattern in patterns:
                if fnmatch.fnmatch(file_, pattern):
                    retValue.append(os.path.join(path, file_))
                    break
        if not recursive: break
    return retValue


# Get all files of the current working directory ending with *.cpp
def getAllFilenames():
    allFiles = getFiles(os.getcwd(), SUFFIX , False) 
    return [ os.path.basename(file_)[:-len(SUFFIX)+1] for file_ in allFiles ]

# Create the CMakeLists.txt header for GCC, Clang, and MSVC
def getHeader():
    return """# Require a recent version of cmake
cmake_minimum_required(VERSION 2.8.4 FATAL_ERROR)

# This project is C++ based.
project(seminar)

if(${CMAKE_CXX_COMPILER_ID} STREQUAL GNU)
    set(CMAKE_CXX_FLAGS "-O3 -std=c++14 -pthread")
    if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
	    message(WARNING "!!!! GCC versions must be at least 5.0 !!!!")
        message(WARNING "!!!! Adust the makefile to build with a different compiler. !!!!")
    endif()
elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL Clang)
    set(CMAKE_CXX_FLAGS "-O3 -std=c++14 -pthread")
    if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.5)
        message(WARNING "!!!! Clang version must be at least 3.5 !!!!")
		message(WARNING "!!!! Adust the makefile to build with a different compiler. !!!!")
    endif()
elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC)
    set(CMAKE_CXX_FLAGS "/Ox /EHsc")
    if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.10)    
        message(WARNING "!!!! MSVC version must be at least 19.10 !!!!")
    endif()	
else()
    message(WARNING "!!! You are using an unsupported compiler! Compilation has only been tested with GCC >= 5.0, Clang >= 3.5, or MSVC >= 19.10. !!!")
endif()
    

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "bin/")

set(CMAKE_VERBOSE_MAKEFILE on)

"""
    
# Create the list of all source files    
def getAllNames(allNames):    
    outNames = "set(example_programs" 
    for entry in allNames:
        outNames += "    " + entry + "\n"
    outNames += "   )"
    outNames += "\n"
    
    return outNames

# Create an executable from the source file
def getTail():
    tail = """foreach(example_program ${example_programs})
  set(sources ${example_program}.cpp)
  source_group("Source Files" FILES{sources})
  add_executable(${example_program} ${sources})
endforeach()"""
    return tail


# Create the CMakeLists.txt
allNames = getAllFilenames()

print >> outputFile, getHeader()

print >> outputFile, getAllNames(allNames)

print >> outputFile, getTail()

 

To understand the program, you should start with the three print statements add the end. The brown font goes directly into CMakeList.txt.

Using the generateCMakeFile.py is straightforward. I copied the  generateCMakeFile.py to the directory, I wanted to use it. This step is not necessary but make the commandline easier to write.

GenerateCmakeFile

Finally, this is the autogenerate CMakeList.txt.

# Require a recent version of cmake
cmake_minimum_required(VERSION 2.8.4 FATAL_ERROR)

# This project is C++ based.
project(seminar)

if(${CMAKE_CXX_COMPILER_ID} STREQUAL GNU)
    set(CMAKE_CXX_FLAGS "-O3 -std=c++14 -pthread")
    if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
	    message(WARNING "!!!! GCC versions must be at least 5.0 !!!!")
        message(WARNING "!!!! Adust the makefile to build with a different compiler. !!!!")
    endif()
elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL Clang)
    set(CMAKE_CXX_FLAGS "-O3 -std=c++14 -pthread")
    if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.5)
        message(WARNING "!!!! Clang version must be at least 3.5 !!!!")
		message(WARNING "!!!! Adust the makefile to build with a different compiler. !!!!")
    endif()
elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC)
    set(CMAKE_CXX_FLAGS "/Ox /EHsc")
    if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.10)    
        message(WARNING "!!!! MSVC version must be at least 19.10 !!!!")
    endif()	
else()
    message(WARNING "!!! You are using an unsupported compiler! Compilation has only been tested with GCC >= 5.0, Clang >= 3.5, or MSVC >= 19.10. !!!")
endif()
    

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "bin/")

set(CMAKE_VERBOSE_MAKEFILE on)


set(example_programs    acquireConsume
    acquireRelease
    asyncLazyEager
    atomicCondition
    atomic
    blockingAsync
    conditionVariable
    dataRaceOnX
    dotProduct
    fetch_mult
    lockGuard
    mutex
    newAlgorithm
    packagedTask
    packagedTaskReuse
    promiseFuture
    promiseFutureSynchronize
    readerWriterLock
    relaxed
    safeInitializationCallOnce
    safeInitializationStatic
    singleton
    spinlockAcquireRelease
    spinLock
    threadArguments
    threadCreate
    threadLifetime
    threadLocal
    threadMethods
    time
    timeDuration
    transitivity
    uniqueLock
   )

foreach(example_program ${example_programs})
  set(sources ${example_program}.cpp)
  source_group("Source Files" FILES{sources})
  add_executable(${example_program} ${sources})
endforeach()

 

2. Generate a Visual Studio Project from CMake

To make my CppDepend life more comfortable, I switch from Linux to Windows and continue with Visual Studio.

MakeVisualProject

This step creates a 64-bit Visual Studio Project in the directory "C:\Users\raine\build". 

3. Import the Visual Studio Project project into CppDepend

Now, it's time to start CppDepend and create a new project Concurrency:

NewProjectNext, I add the Visual Studio to my new project.

solution

One step is still missing. I have to press the analyse button. It took my eight cores a few second to analyse the source files.

4. Make the code analysis in CppDepend

As I already assumed it, the most checks are green:

analysis

Of course, I'm only interested in the red (Error) and yellow (Warning) ones.

Error:

  • Quality Gates:
    • This issue boils down to mainly one function: The method is to big, has too many local variables, and is poorly documented. What I liked about CppDepend was that it shows me the metric I violated. 

QualityGates

Warnings:

  • Code Smells:
    • The previous break of the Quality Gates was also a Code Smell; therefore, I can ignore this warning.
  • Object Oriented Design:
    • Only one of the thirty rules was violated: three of my constructors taking one argument were not explicit. Honestly, this is a rule I often preach in my seminars. Shame on me. 

ExplicitConstructor

 

  •  Misc:
    • This is also an issue I should fix. I passed a std::string by copy.

passByReference

  • Dead Code:
    • This violation was funny. The violation was that the destructor of the singleton was never called. To my excuse, I only used a singleton to explain the thread-safe initialisation of a variable.
  • Naming Conventions:
    • I'm not a big fan of Hungarian notation. Therefore, I wouldn't prefix an instance variable with a m_ or a static with a s_.

My Resumee

Here is my short resume. I put not too much effort in the analysis of CppDepend but more in establishing an automated process to create the Visual Studio solution as input for CppDepend. Regardless, I found immediately a few big issues which I'm going to fix. It's quite comfortable to fix these issues because a double-click on the Error/Warning in CppDepend opens the source file in Visual Studio.

Although I only scratched at the surface of CppDepend, the analysis of my concurrency source files impressed me. I will, therefore, invest in the future more time to better understand the various metrics of CppDepend and apply them to my other source files. My gut feeling is that I can draw many benefits from CppDepend.

 

 

 

Thanks a lot to my Patreon Supporters: Eric Pederson, Paul Baxter,  Meeting C++, Matt Braun, Avi Lachmish, Roman Postanciuc, Venkata Ramesh Gudpati, Tobias Zindl, Dilettant, Marko, Ramesh Jangama, and Emyr Williams.

 

Thanks in particular to:  TakeUpCode 450 60

 

Get your e-book at Leanpub:

The C++ Standard Library

 

Concurrency With Modern C++

 

Get Both as one Bundle

cover   ConcurrencyCoverFrame   bundle
With C++11, C++14, and C++17 we got a lot of new C++ libraries. In addition, the existing ones are greatly improved. The key idea of my book is to give you the necessary information to the current C++ libraries in about 200 pages.  

C++11 is the first C++ standard that deals with concurrency. The story goes on with C++17 and will continue with C++20.

I'll give you a detailed insight in the current and the upcoming concurrency in C++. This insight includes the theory and a lot of practice with more the 100 source files.

 

Get my books "The C++ Standard Library" (including C++17) and "Concurrency with Modern C++" in a bundle.

In sum, you get more than 600 pages full of modern C++ and more than 100 source files presenting concurrency in practice.

 

Get your interactive course

 

Modern C++ Concurrency in Practice

C++ Standard Library including C++14 & C++17

educative CLibrary

Based on my book "Concurrency with Modern C++" educative.io created an interactive course.

What's Inside?

  • 140 lessons
  • 110 code playgrounds => Runs in the browser
  • 78 code snippets
  • 55 illustrations

Based on my book "The C++ Standard Library" educative.io created an interactive course.

What's Inside?

  • 149 lessons
  • 111 code playgrounds => Runs in the browser
  • 164 code snippets
  • 25 illustrations

Comments   

0 #1 Yacob Cohen-Arazi 2019-01-02 18:48
I agree. cppdepend has benefits. I added it to my clion. so now I have tidy+depend running.
Quote

Add comment


Subscribe to the newsletter (+ pdf bundle)

Blog archive

Source Code

Visitors

Today 2153

All 2177341

Currently are 137 guests and no members online

Kubik-Rubik Joomla! Extensions

Latest comments