I have a number of algorithms for community detection on graphs that I want to now visualise them. This visualisation requires me to 'hijack' these algorithms while they execute and log what they are doing. Specifically this will mean passing a reference to a std::vector<graph_partition>
as an argument to these algorithms, and appending to that vector as the algorithm proceeds.
Therefore to each algorithm (which are typically just functions), I would need to add a further argument for the &std::vector<graph_partition>
, and one or two lines of code for the logging.
I will not always want/need to log however, and so doing this in an intelligent way has proved non-trivial. I have thought of:
&std::vector<graph_partition>
when I don't want to use it? Also (probably minuscule) runtime hit of continuously evaluating conditional.Any ideas or thoughts on these would be appreciated.
If you fancy using templates, I don't think you really need variadic templates. If you're happy to recompile in order to switch logging on and off:
struct NoLogging {
void log(const graph_partition &) {}
};
struct Logging {
std::vector<graph_partition> vec;
void log(const graph_partition &p) {
vec.push_back(p);
}
};
template <typename Logger>
void some_algorithm(Logger &logger) {
// do some stuff
logger.log(something);
}
// optionally, for convenience
void some_algorithm() {
NoLogging l;
some_algorithm(l);
}
// user writes:
some_algorithm();
// or
Logging l;
some_algorithm(l);
// do something with l.vec
The difference between this and "just log by default, even if I don't need it", is that an even vaguely decent compiler will completely remove the calls to log
in some_algorithm<NoLogging>
, because it can see that they do nothing.
If you don't want to have to recompile, you could have a runtime switch between the two different sets of instantiations - it may or may not be convenient to do this via some polymorphic interface that provides all the algorithms and has two derived classes, from a template like so:
template <typename Logger>
struct ConcreteAlgorithms : public Algorithms {
Logger logger;
static void some_algorithm() {
::some_algorithm(logger);
}
// more algorithms
};
Algorithms *get_algorithms(bool with_logging) {
if (with_logging) {
return new ConcreteAlgorithms<Logging>;
} else {
return new ConcreteAlgorithms<NoLogging>;
}
}
However, at this point you're going to have the code bloat of two different versions of the algorithms, so you might prefer to make the logger polymorphic and take the (probably tiny) runtime overhead instead, as per Mark's answer.