Skip to content

Add a Snowplow interface with the ability to initialize and manage multiple trackers #22

@OskarSigvardsson

Description

Hi! I work for a company that makes and sells audio plugins, and we're looking at different solutions for adding tracking to our apps.

The thing with audio plugins is that they are (usually) dynamically loaded into DAWs ("digital audio workstations", the audio plugin hosts), and it is a very common occurance to have a single audio plugin loaded multiple times in the same process (for instance: if you have an audio plugin that works as some kind of filter or audio effect, you may want that filter on several different tracks).

Snowplow seems like a very nice solution for this, except for one thing: the "global singleton" nature of the tracker. That's not ideal for us, it would be really nice to load one tracker for each plugin, if possible.

I was looking through the code an seeing if that was possible: seemingly it is: if you create multiple trackers with Tracker::init, the first one gets stored as the global singleton, but it's not an error to create multiple ones, you just have destroy it yourself (and not use Tracker::instance). I realize that the Storage class is also a global singleton, but I figure that's less of an issue, since it's locked with a mutex, and I plan on passing the same database path for every instance.

However, when implementing this solution, the compiler informed me of another problem: the Tracker destructor is private, so you literally can't manage it yourself: the only way to destroy a Tracker is through Tracker::close(), which destroys the singleton.

One solution to this is simply making the Tracker destructor (and constructor as well, while we're at it) public, and let me totally ignore the singleton functions, they serve no purpose for me anyway. However, at this point, I would like to get advice from you if this would be a good change. Is there any reason you can think of why this would be a problem?

I understand you wanting to offer a singleton as a convenience, but forcing its use like you do now is very thing for a library to do: for many kinds of applications, it's a very dangerous thing to use. There's a reason Singletons are considered an anti-pattern by most authorities.

Incidentally: if you are going to force the use of a global singleton, there are a few fixes that should probably be done:

  1. Tracker::init should throw an exception if the singleton already exists (since the only way to delete the class is by deleting the singleton, it should be an error to create any other object).
  2. The singleton should not have the type Tracker*, it should be a std::unique_ptr<Tracker>, to ensure that it is always uninitialized.

Metadata

Metadata

Labels

priority:mediumOn the roadmap.type:enhancementNew features or improvements to existing features.

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions