0

I'm in the process of building an application which needs extensive logging of points in time at which certain events happen. For example when it renders a sequence of different shapes to the screen then we need a logfile with info like 'shape A rendered to surface at time X, shape B rendered to surface at time Y, surface flipped at time C' etc. in response to comments These names (A,B) should be meaningful and will either be user-defined (e.g. 'MyGroupOfRectangles') or automatically generated (like shape type + number, e.g. 'Rectangle1' or for bitmaps/avis/... use the filename). Main reason for this is that most of the time the logfile is going to be interpreted simply by the user reading it and quickly checking out if the sequence is doing what he/she wants to.

In order to make this happen it's clear that at the moment the log function is invoked it needs to know the name of the shape it's going to log about. One could just give any type of object which is going to be logged about a string member. However thinking about that there are some implementation- and design-wise questions which make me doubt that approach. But other approaches in turn have other problems.

The major question being (almost philosophical - I'm probably overthinking this): should an object know it's own name? After all it's merely an implementation detail here, used solely for logging - if I wouldn't want to log Shapes by their name they wouldn't need to be named at all.

So does it make sense to stuff the name in there as well, or would it be a better practice to keep a map somewhere which links object instances to a name? Something like

Map< Shape, string > shapeNames;

string NameForShape( Shape s )
{
  return shapeNames.Contains( s ) ? shapeNames[ s ] : "<unnamed>";
}

This is very much unintrusive but does introduces some other potential problems in keeping the map up to date with existing shapes: should a shape be destructed, it's name also has to be erased from the map.

The other option (or if there are multiple other options, by all means let me know) is to make a Shape returns it's name. Being an interface it could be like this:

interface Shape
{
  void Render();
  ...
  string Name();
}

However this means every implementation has to take care of providing the Name() implementation and they're likely to going to be doing that all in the same way, like

class Rect : Shape
{
public:
  Rect( string name ) : { this->name = name; }

  string Name() { return name; }

private:
  string name;
}

That's quite some boilerplate and DRY is a holy rule so raises more questions: do I get rid of the pure interface and make it abstract instead (not ideal for unit testing)? Or do I add an extra layer with a default Shape implementation like NamedShape which partially implements Shape and just provides Name() (extra inheritance layer is not ideal either)?

  • in Java, Object.toString() would answer a question like your "yes, an object should know it's own name". But your seem to be about C++... – gnat Nov 6 '14 at 10:07
  • C++ indeed - the pseudocode doesn't really reflect that though – stijn Nov 6 '14 at 10:36
  • What are the semantics of these names? Are they just to distinguish different instances of the same class in the log, or do they actually have some meaning in the rest of your program? – Useless Nov 6 '14 at 11:07
  • @Useless as it stands now they are just used to distinguish instances for logging. The user part of the application is a Python script so the instances which need it already have a name bound to them. – stijn Nov 6 '14 at 11:20
  • If you only need uniqueness (and the objects are long-lived) you can just log the pointer value of this. – Useless Nov 6 '14 at 11:23
2

What you'd like to have is a mixin, or a trait, but you can't have it since your language of choice doesn't support it. So you are looking for ways to emulate it.

Personally I agree that deriving an object's name shouldn't be its own responsibility. For one, because if your naming convention changes, you have to change your Rect.Name implementation. So now the Rect class an extra reason to change, which violates SRP.

Custom implementation of Object.toString - brought up by @gnat in comments - can be tremendously helpful for debugging, but - perhaps it's just my bias - I wouldn't put anything actually important, any data worth of being persisted, into toString.

Transferring the responsibility of naming objects in a clear manner into a class of its own sounds like a good idea.

Does it really have to keep track of every shape in existence, though? That's what worries you, and I'm not sure why that would be a requirement.

Couldn't this class simply derive reasonable names for any given Shape on the fly? I may be missing some context here. You always know what type any particular Shape instance is. Where are the A, B, C identifiers coming from?

  • re tracking shapes: if they are not tracked the map just grows when new shapes are created but never shrinks; re identifiers: the shapes are defined mostly in a scripting language on top of the app. Some shapes are actually just read from bitmaps so yes their name can de derived. However other shapes are just rectangles/circles and moreover there are groups of shapes etc. Generating random names for them is ok if there's only a couple of them, but in to properly understand the logfile it's going to be needed to give them actual names. – stijn Nov 6 '14 at 10:42
  • @stijn and these actual names come from user input? Or would you like to generate them to reflect the "order of apperance" (like A, B, Z, AA, AB etc.) – Konrad Morawski Nov 6 '14 at 10:43
  • Can be both actually. For complicated things they are nearly always going to be from user input else it's impossible to interpret the logs. For simple things (say just showing a rectangle and a circle) we're not going to bother with names and will indeed autogenerate something like Rect1 and Circle1 – stijn Nov 6 '14 at 10:54
  • @stijn wouldn't it be confusing to reuse the same identifier (eg. Rect1) for another rectangle even after the original Rect1 doesn't exist anymore? If you'd agree that it would, then don't worry about removing dead entries from your Map, only make it Map<int, string> instead and use hash codes as keys (to minimize the memory footprint). And just let it grow. Never reuse the same name. – Konrad Morawski Nov 6 '14 at 11:57
  • depends. Normally yes, reusing ids is confusing. On the other hand for longer runs it might be ok. Suppose we start displaying a rect for 5 minutes, then shict to a circle and then an hour later swicth to a rect again it's no problem if that second rect is also called Rect1 (gven that the first rect has been destroyed already) – stijn Nov 6 '14 at 12:50
2

so, to recap:

  1. your shapes have actual names, determined by some outside entity (possibly user input), and aren't always just Circle1 etc.
  2. you don't think the shape should be responsible for this property (namedness? nominativity?)

Well, assuming you don't use a global logger and the logger itself has a sane interface, you can create a named logger proxy: either that's passed to the Shape constructor, or you wrap them both in a NamedShape which passes the proxy to each forwarded method.

Then, every line logged to the proxy is just forwarded to the real logger, with the name prepended.

0

Why don't you just use RTTI to get a string representation of the class name directly? Yes, it is implementation defined, so you can't depend on it being some exact thing if you are switching compilers. However if you need human readable logging, this is an excellent solution that doesn't involve adding a line of code outside the Logger class itself.

https://stackoverflow.com/questions/1024648/retrieving-a-c-class-name-programatically

  • We normally don't use RTTI, but does this give a unique name for every instance of the class? – stijn Nov 7 '14 at 7:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.