Avoiding dependencies is exploding the number of p

2019-07-08 23:50发布

I'm working in C++; I'm new to Visual Studio and still trying to understand how to use it effectively.

My problem is that I have what seems to me a fairly small, non-complex project, but I find myself adding more and more Projects to the Solution, and managing them is becoming unwieldy and frustrating.

  • The project depends on a device, so I've defined DeviceInterface, and I've got FakeDevice and RealDevice implementing the interface.
  • My core project Foo, I've written is a static library, defined using DeviceInterface. The Foo library isn't familiar with either of the concrete implementations.
  • I have multiple test executables, let's call them TestExe1, TestExe2, and so forth. These tests share some common code, FooTestUtils.
  • Using RealDevice requires some init and teardown work before and after use. This doesn't belong within the interface implementation; the client code is naturally responsible for this.
    • This means that a test-executable is only capable of running using RealDevice if I put in a strong dependency on RealDevice and the init/teardown resources. Which I don't need or want for tests using the Fake.
    • My present solution is to split test executables up - one for FakeDevice, another for RealDevice that performs the initialization and then goes and calls the same test code.

TL;DR: Core library Foo, depending on DeviceInterface, which has multiple implementations. Multiple test executables, most of which can work with either implementation of DeviceInterface, but one of those implementations requires extra set-up in the client code.

This seems to me like a reasonable level of complexity. But it results in SO MANY Projects:

  • Static Libraries:
    • Foo
    • RealDevice implementation
    • FooTestUtils (note: includes FakeDevice implementation)
    • gtest (used for some of the testing)
    • Library from another solution, needed for RealDevice use
  • Executables:
    • 2 TestExe$i projects for every test executable I want

In the *nix environments I'm more used to, I'd divide the code into a reasonable directory tree, and a lot of these "Projects" would just be a single object file, or a single .cpp with some client code for the core logic.

Is this a reasonable number of projects for a solution of this scope? It feels like an awful lot to me. Frequently I find some setting I need to change across half a dozen different projects, and I'm finding it increasingly difficult to navigate. At present, this is still manageable, but I'm not seeing how this will remain workable as I proceed into larger, more complex projects. Could I be organizing this better?

(Again, I'm new to Visual Studio, so the problem might be that I don't know how to manage multiple related projects, rather than just the number of the projects themselves.)

1条回答
ゆ 、 Hurt°
2楼-- · 2019-07-09 00:45

Though what your doing is pretty standard - and for a small project like you are describing you solution seems perfectly standard. However Visual studio does provide some ways to minimize the impact of these issues for experienced developers:

build-configurations and property-sheets:
In short, why have a project for fakeDevice and RealDevice?

Create a project for "Device", that depending on what configuration is chosen builds the sources of fakeDevice, or those of RealDevice. This also allows you to start your project in "testing" configuration, and automatically load the fakeDevice, meanwhile selecting "Debug" or "release" would provide the RealDevice.

Note that both Projects, as well as the entire solution may have configurations independently - allow for rapid batch-building of specific configurations.

real world example
My company produces a plugin for adobe-illustrator, there are seven supported versions of Adobe (each with it's own SDK), as well as 32 and 64bit variants, and further debug and release builds (and double that again to 28+ variants as there are two near-identical branded versions of the plugin). My Solution is as follows: Plugin-Solution [Debug][Release] / (win32/x64) Plugin [Debug AI4][Debug AI5][Debug AI6][Debug AI7][Release AI4] [Release AI5][Release AI6][Release AI7] / (win32/x86) {libraries with similar setups...} In my day to day operation, I simple "press play" in the debug config, however when release time comes (or a specific version needs testing) I "Batch Build" the correct combination of projects for debugging, or packaging.

This effectively means, although I have (including shared libraries) near-enough 30 binaries being produced for a release, my solution only had three projects within it.

testing executables
As for the unit-testing executables, I'd recommend creating a separate solution for those - Visual studio has no problem having several solutions open concurrently - I do however have one tip

Create a separate solution and create all your unit tests within it, then in your main solution add a simple "tests" project, in it's post-build event run a powershell/batch script.

That script can then invoke the MSVCC toolchain on the unit-tests solution, and then run the tests collating the results (if your in the correct configuration). This will allow you to build/run your tests from a single project, even if you do need to alt+tab to create a new unit test.

Personal (opinionated) Advice
Having developed in 'Nix, Windows, and Apple systems, here's a good metaphore of layout. 'Nix expects you to create your own makefiles and folder layout, it assumes you know exactly what your doing (in the terminal!) and the layout becomes your plaything (with enough shellscripts).
Windows/Visual studio is designed to be open to every level of user, an eight-yearold learning programs on visual-basic, or a experienced C++ developer creating hardware-drivers. As such the interface is designed to be very Expandable - "projects" in "solutions" is a basic idea (many beginners don't realise you can have multiple projects. However if you want more options, there is one way to do it as far as MS is concerned (in this case, configurations and property sheets) - if your writing a makefile or creating a layout you are "doing it wrong" (in microsoft's eyes anyway).

If you take a look at the hassle boost has had fitting into the windows ecosystems over the last few years, you'll tend to understand the problem. On 'nix having several dozen shared-libraries from a package apt/yum installed as a dependency is fine! Windows however (feels like) having more than one DLL is a bad idea. There's no package-manager, so either rely on .Net, or package a single boost-dll with your product. (this is why I prefer static linking on windows).

EDIT:
When you have multiple configurations, selecting what sources do and don't build for each can be done in two fashions.

one: manually
Right clicking on any source-file in the solution explorer and selecting properties - Under the "general" section, you can select "Excluded From Build" (this works if you group-select and rightclick also.

two: XML magic
If you open the vcxproj file, you'll fine a well-formed XML file layout! While handling the exact conditions of managing inclusions, exclusions, and even other options is beyond the scope of this post, basic detailscan be found In this well worded stackoverflow question as well as the MDSN toolchain documentation

查看更多
登录 后发表回答