0

In our project (C++14) we have divided our software into several components by a functional breakdown of the system. Each of the modules reside in their own namespace embedded in a common namespace for the system. We use CMake as our build system and each component is a static library that can be built separately and is linked together at the end.

Now, in many components specific data types are defined, as class or struct e.g. for time, a collection of data fields to be processed together and so on. These data structures are defined locally at the component where the data they contain is created.

But. When I now have to access one of these data structures from other components I have to include the header from the specific component and have a dependency between these two components. And as this is a common approach, we have many dependencies between our software components easily leading to cyclic dependencies. :(

In the C world I would have created a GlobalDataStructures.h and added all the data structures that are used throughout the software system.

What is the (modern) C++ approach to this? What are best practices?

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
Questmaster
  • 108
  • 1
  • 9

2 Answers2

1

The idea behind the “C style” approach is basically sound.

As a general rule you want to define your data structures as far away from the root of the dependency tree as possible. If a cycle happens some of the data structures must move up at least one level towards the root to break the cycle.

At some point you end up at the dependency root. For a project with different components that means introducing a root component. It’s a component – in your case a static lib – like any other and holds data structures and functionality necessary and/or useful for the whole system. The tricky part is to not let that component become the cupboard under the kitchen sink – after all now you have a convenient place to put things without having to think about where they really belong. But that’s a people problem, not a technical one.

Usually I call the CMake target for the root component library projectname_core. I used to put all its contents into the projectname::core namespace. But it turned out that everybody in the team just wrote using namespace projectname::core; everywhere. Apparently the extra namespace doesn’t add any useful information, and putting the system-wide stuff into the projectname namespace works just as well. That’s what I do these days.

besc
  • 2,507
  • 13
  • 10
0

This is my area of expertise. I am the creator of POWER, the software architecture which models manufacturing. You can read my post on DTOs here, since I've gone beyond mere hardcoded ones. What is Data Transfer Object?

Now to answer your question...notice in distributed (OOP) architecture, you need to either attach the whole namespace to use the DTO, duplicate the DTO into each consuming namespace or centralize the DTO into one shared namespace. In POWER, however, a process in one namespace can operate on an object instance it doesn't even know about, because the process is bound to the DTO's reference properties rather than the DTO (and its type) itself. POWER is a zero coupling architecture, because manufacturing is too.

Technically, processes should never share DTOs even if the DTOs are highly similar or even structurally equivalent. Sharing modules or even DTOs creates centralization complexity, an organization flaw I authoritatively describe here using Harvard Business Review as a source: http://www.powersemantics.com/p.html

RBJ
  • 128
  • 6
  • As far as I understand DTOs are targeted towards distributed applications. This is not what I had in mind in my described scenario. But thanks for the hint to the pattern. – Questmaster May 01 '20 at 19:35