creating unique message identifiers

Discussion in 'C++' started by mliptak, Nov 4, 2008.

  1. mliptak

    mliptak Guest

    I'm trying to implement logging in my application, so that each log
    message has its unique identifier, e.g.
    log(identifier, text)
    What I want to achieve is that the compiler screams if the log() with
    'identifier' is also used in some other place in the code which would
    make the 'identifier' not unique.
    Is that something that can be achieved in C++?
    Thanks
     
    mliptak, Nov 4, 2008
    #1
    1. Advertising

  2. mliptak

    James Kanze Guest

    On Nov 4, 11:54 am, mliptak <> wrote:
    > I'm trying to implement logging in my application, so that
    > each log message has its unique identifier, e.g.
    > log(identifier, text)
    > What I want to achieve is that the compiler screams if the
    > log() with 'identifier' is also used in some other place in
    > the code which would make the 'identifier' not unique.
    > Is that something that can be achieved in C++?


    It depends on the type of identifier, but in general, yes,
    provided log is a macro. In the general case, you need a local
    static variable to achieve it, and the actual identifier won't
    be known until compile time. If identifier is text, however,
    you can generate it with a macro using __FILE__ and __LINE__.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Nov 4, 2008
    #2
    1. Advertising

  3. mliptak

    mliptak Guest

    On Nov 4, 2:21 pm, James Kanze <> wrote:
    > On Nov 4, 11:54 am, mliptak <> wrote:
    >
    > > I'm trying to implement logging in my application, so that
    > > each log message has its unique identifier, e.g.
    > > log(identifier, text)
    > > What I want to achieve is that the compiler screams if the
    > > log() with 'identifier' is also used in some other place in
    > > the code which would make the 'identifier' not unique.
    > > Is that something that can be achieved in C++?

    >
    > It depends on the type of identifier, but in general, yes,
    > provided log is a macro.  In the general case, you need a local
    > static variable to achieve it, and the actual identifier won't
    > be known until compile time.  If identifier is text, however,
    > you can generate it with a macro using __FILE__ and __LINE__.


    What if I want to set the identifier myself, not have it generated at
    compile time (using local static variable)?

    I was also thinking of using the __FILE__ and __LINE__ but the problem
    is this could change when the code changes, and I need to have the
    identifier fixed for each message.
     
    mliptak, Nov 4, 2008
    #3
  4. On Nov 4, 10:54 am, mliptak <> wrote:
    > I'm trying to implement logging in my application, so that each log
    > message has its unique identifier, e.g.
    > log(identifier, text)


    What do you need that identifier for?

    > What I want to achieve is that the compiler screams if the log() with
    > 'identifier' is also used in some other place in the code which would
    > make the 'identifier' not unique.


    It is not possible in the general case when a binary is built by
    linking several object files compiled separately. In this case you
    could make the linker produce an error, however, it still would not be
    possible in the presence of dynamic libraries.

    --
    Max
     
    Maxim Yegorushkin, Nov 4, 2008
    #4
  5. mliptak

    mliptak Guest

    On Nov 4, 2:53 pm, "Victor Bazarov" <> wrote:
    > mliptak wrote:
    > > On Nov 4, 2:21 pm, James Kanze <> wrote:
    > >> On Nov 4, 11:54 am, mliptak <> wrote:

    >
    > >>> I'm trying to implement logging in my application, so that
    > >>> each log message has its unique identifier, e.g.
    > >>> log(identifier, text)
    > >>> What I want to achieve is that the compiler screams if the
    > >>> log() with 'identifier' is also used in some other place in
    > >>> the code which would make the 'identifier' not unique.
    > >>> Is that something that can be achieved in C++?

    >
    > >> It depends on the type of identifier, but in general, yes,
    > >> provided log is a macro. In the general case, you need a local
    > >> static variable to achieve it, and the actual identifier won't
    > >> be known until compile time. If identifier is text, however,
    > >> you can generate it with a macro using __FILE__ and __LINE__.

    >
    > > What if I want to set the identifier myself, not have it generated at
    > > compile time (using local static variable)?

    >
    > > I was also thinking of using the __FILE__ and __LINE__ but the problem
    > > is this could change when the code changes, and I need to have the
    > > identifier fixed for each message.

    >
    > I think you're still to figure out exactly what you need (i.e. the
    > "requirements" part of your design).  What does it mean "to have the
    > identifier fixed for each message"?  Do you intend to have the same
    > message sprinkled throughout your code and have the same identifier
    > accompany it?  If the messages are unique, why nave an identifier at
    > all?  If the messages aren't unique and can be the same, using the
    > exact location in your code to generate the identifier is a nice way
    > to ensure uniqueness of them.


    What I meant by this is to have the "code" associated with particular
    "text" and this association never changes (which is not true if I'd be
    using __FILE__ and __LINE__).

    You asked why have identifier at all.. Well another requirement is to
    have the "code" unique throughout the program.

    Example:

    // a.cc
    // ok
    log(code1, "text1");
    log(code2, "text2");
    ....
    // invalid - code1 already used before
    log(code1, "text3");

    // b.cc
    // invalid - code1 already used elsewhere
    log(code1, "text4");
    // ok
    log(code4, "text5");

    >
    > The presence of __FILE__ and __LINE__ is very convenient if you need
    > to later find which of the millions of lines in your code caused the
    > output of the message.
    >
    > Another way would be a global counter of sorts.  You could create
    > a class that when instantiated would increment some counter and when
    > output would produce that counter (which it would keep as a static
    > data member), but then you lose control over what counter relates to
    > which line since generation of the counter can depend on the execution
    > order of the constructors for those objects.
    >


    As I mentioned before, the disadvantage of this approach is that the
    codes of messages change when the source changes.

    > Go with __FILE__ and __LINE__.  Trust us, we've done it before.
    >
    > V
     
    mliptak, Nov 4, 2008
    #5
  6. mliptak

    mliptak Guest

    On Nov 4, 2:54 pm, Maxim Yegorushkin <>
    wrote:
    > On Nov 4, 10:54 am, mliptak <> wrote:
    >
    > > I'm trying to implement logging in my application, so that each log
    > > message has its unique identifier, e.g.
    > > log(identifier, text)

    >
    > What do you need that identifier for?


    The requirement is to have the log messages searchable easily, by
    tools like grep. It is a bit more convenient than search by message
    text.

    >
    > > What I want to achieve is that the compiler screams if the log() with
    > > 'identifier' is also used in some other place in the code which would
    > > make the 'identifier' not unique.

    >
    > It is not possible in the general case when a binary is built by
    > linking several object files compiled separately. In this case you
    > could make the linker produce an error, however, it still would not be
    > possible in the presence of dynamic libraries.


    Yes, I thought about the dynamic libraries before. In my case it is
    ok if it does not work for dynamic libraries as I don't use them right
    now.

    >
    > --
    > Max
     
    mliptak, Nov 4, 2008
    #6
  7. mliptak

    mliptak Guest

    On Nov 4, 3:48 pm, Hendrik Schober <> wrote:
    > mliptak wrote:
    > > On Nov 4, 2:21 pm, James Kanze <> wrote:
    > >> On Nov 4, 11:54 am, mliptak <> wrote:

    >
    > >>> I'm trying to implement logging in my application, so that
    > >>> each log message has its unique identifier, e.g.
    > >>> log(identifier, text)
    > >>> What I want to achieve is that the compiler screams if the
    > >>> log() with 'identifier' is also used in some other place in
    > >>> the code which would make the 'identifier' not unique.
    > >>> Is that something that can be achieved in C++?
    > >> It depends on the type of identifier, but in general, yes,
    > >> provided log is a macro.  In the general case, you need a local
    > >> static variable to achieve it, and the actual identifier won't
    > >> be known until compile time.  If identifier is text, however,
    > >> you can generate it with a macro using __FILE__ and __LINE__.

    >
    > > What if I want to set the identifier myself, not have it generated at
    > > compile time (using local static variable)?

    >
    > > I was also thinking of using the __FILE__ and __LINE__ but the problem
    > > is this could change when the code changes, and I need to have the
    > > identifier fixed for each message.

    >
    >   Why don't you want to use '__FILE__' and '__LINE__'? If these
    >   change too often, add '__DATE__' and '__TIME__' which, together
    >   with your version management should be enough.


    I think it is too complicated for what I really need to achieve.
    What I had in mind originally was something like:

    log(ERROR01, "Text of error 1");
    log(ERROR02, "Text of error 2");

    >   OTOH, I have written a logging library which allows grouping of
    >   code and which emits the group's tag name. You can have a look
    >   at that athttp://sourceforge.net/projects/templog/. (The main
    >   goal of this was to use expression templates to gain performance
    >   when logging is turned off and I haven't found the time yet to
    >   document anything, so I apologize if it seems a bit cryptic.)
    >
    >   HTH,
    >
    >   Schobi
     
    mliptak, Nov 4, 2008
    #7
  8. On Nov 4, 3:05 pm, mliptak <> wrote:
    >
    > What I meant by this is to have the "code" associated with particular
    > "text" and this association never changes (which is not true if I'd be
    > using __FILE__ and __LINE__).
    >
    > You asked why have identifier at all.. Well another requirement is to
    > have the "code" unique throughout the program.
    >
    > Example:
    >
    > // a.cc
    > // ok
    > log(code1, "text1");
    > log(code2, "text2");
    > ...
    > // invalid - code1 already used before
    > log(code1, "text3");


    If that line had read
    log(code1, "text1"),
    would that have been OK?

    >
    > // b.cc
    > // invalid - code1 already used elsewhere
    > log(code1, "text4");
    > // ok
    > log(code4, "text5");
    >


    If you want to have a consistent mapping between a numeric/symbolic ID
    and a string, the worst thing you can do is require the programmer to
    provide both the ID and the string.

    The solution I would propose is to use only the ID in the log
    statements, and use a separate mechanism (for example a table) to map
    the ID to a string.
    For example, something like this:

    enum logID {
    code1,
    code2,
    code4
    };

    char const * const logString[] = {
    "text1",
    "text2",
    "text5"
    };

    void log(enum logID id)
    {
    char const * const text = logString[id];
    // write text to log destination
    }
    //usage: log(code1);

    To keep the mapping between the ID's and the texts consistent, you can
    generate the enumeration and the table from a single source. This
    could even be done with the preprocessor.

    Bart v Ingen Schenau
     
    Bart van Ingen Schenau, Nov 4, 2008
    #8
  9. mliptak

    peter koch Guest

    On 4 Nov., 15:59, mliptak <> wrote:
    > On Nov 4, 3:48 pm, Hendrik Schober <> wrote:
    >
    >
    >
    >
    >
    > > mliptak wrote:
    > > > On Nov 4, 2:21 pm, James Kanze <> wrote:
    > > >> On Nov 4, 11:54 am, mliptak <> wrote:

    >
    > > >>> I'm trying to implement logging in my application, so that
    > > >>> each log message has its unique identifier, e.g.
    > > >>> log(identifier, text)
    > > >>> What I want to achieve is that the compiler screams if the
    > > >>> log() with 'identifier' is also used in some other place in
    > > >>> the code which would make the 'identifier' not unique.
    > > >>> Is that something that can be achieved in C++?
    > > >> It depends on the type of identifier, but in general, yes,
    > > >> provided log is a macro.  In the general case, you need a local
    > > >> static variable to achieve it, and the actual identifier won't
    > > >> be known until compile time.  If identifier is text, however,
    > > >> you can generate it with a macro using __FILE__ and __LINE__.

    >
    > > > What if I want to set the identifier myself, not have it generated at
    > > > compile time (using local static variable)?

    >
    > > > I was also thinking of using the __FILE__ and __LINE__ but the problem
    > > > is this could change when the code changes, and I need to have the
    > > > identifier fixed for each message.

    >
    > >   Why don't you want to use '__FILE__' and '__LINE__'? If these
    > >   change too often, add '__DATE__' and '__TIME__' which, together
    > >   with your version management should be enough.

    >
    > I think it is too complicated for what I really need to achieve.
    > What I had in mind originally was something like:
    >
    > log(ERROR01, "Text of error 1");
    > log(ERROR02, "Text of error 2");
    >


    Certainly, if you search for simplicity using __FILE__ and __LINE__ is
    the way to go. I once used your approach (manually and written in C
    loads of years ago), but the reason not to use __LINE__ and __FILE__
    was because of a restricted environment where low space overhead was
    at a premium. (It was not for logging but for assertions, and in case
    of an assert, the integer id was simply dumped).

    /Peter
     
    peter koch, Nov 4, 2008
    #9
  10. mliptak

    mliptak Guest

    On Nov 4, 5:15 pm, Bart van Ingen Schenau
    <> wrote:
    > On Nov 4, 3:05 pm, mliptak <> wrote:
    >
    >
    >
    >
    >
    > > What I meant by this is to have the "code" associated with particular
    > > "text" and this association never changes (which is not true if I'd be
    > > using __FILE__ and __LINE__).

    >
    > > You asked why have identifier at all.. Well another requirement is to
    > > have the "code" unique throughout the program.

    >
    > > Example:

    >
    > > // a.cc
    > > // ok
    > > log(code1, "text1");
    > > log(code2, "text2");
    > > ...
    > > // invalid - code1 already used before
    > > log(code1, "text3");

    >
    > If that line had read
    >   log(code1, "text1"),
    > would that have been OK?
    >
    >
    >
    > > // b.cc
    > > // invalid - code1 already used elsewhere
    > > log(code1, "text4");
    > > // ok
    > > log(code4, "text5");

    >
    > If you want to have a consistent mapping between a numeric/symbolic ID
    > and a string, the worst thing you can do is require the programmer to
    > provide both the ID and the string.
    >
    > The solution I would propose is to use only the ID in the log
    > statements, and use a separate mechanism (for example a table) to map
    > the ID to a string.
    > For example, something like this:
    >
    > enum logID {
    >   code1,
    >   code2,
    >   code4
    >
    > };
    >
    > char const * const logString[] = {
    >   "text1",
    >   "text2",
    >   "text5"
    >
    > };
    >
    > void log(enum logID id)
    > {
    >   char const * const text = logString[id];
    >   // write text to log destination}
    >
    > //usage: log(code1);
    >
    > To keep the mapping between the ID's and the texts consistent, you can
    > generate the enumeration and the table from a single source. This
    > could even be done with the preprocessor.
    >
    > Bart v Ingen Schenau


    Yes, I was thinking about this, but the problem is the "text" is not
    only a text, but string with parameters, either:
    "text param %d, %s", a, b
    or:
    "text param " << a << ", " << b

    I think I will end up with:
    void log(enum logID id, ...)
    {
    }
    and do the vsnprintf() magic in there.
     
    mliptak, Nov 5, 2008
    #10
  11. mliptak

    James Kanze Guest

    On Nov 4, 2:31 pm, mliptak <> wrote:
    > On Nov 4, 2:21 pm, James Kanze <> wrote:


    > > On Nov 4, 11:54 am, mliptak <> wrote:


    > > > I'm trying to implement logging in my application, so that
    > > > each log message has its unique identifier, e.g.
    > > > log(identifier, text) What I want to achieve is that the
    > > > compiler screams if the log() with 'identifier' is also
    > > > used in some other place in the code which would make the
    > > > 'identifier' not unique. Is that something that can be
    > > > achieved in C++?


    > > It depends on the type of identifier, but in general, yes,
    > > provided log is a macro. In the general case, you need a
    > > local static variable to achieve it, and the actual
    > > identifier won't be known until compile time. If identifier
    > > is text, however, you can generate it with a macro using
    > > __FILE__ and __LINE__.


    > What if I want to set the identifier myself, not have it
    > generated at compile time (using local static variable)?


    You mean that you want the user to provide an indentifier, that
    the compiler checks for uniqueness? The compiler can't do that
    per se (at least not within a single statement), but you could
    easily use it as the initializer of a local static variable,
    which would check for uniqueness at runtime.

    > I was also thinking of using the __FILE__ and __LINE__ but the
    > problem is this could change when the code changes, and I need
    > to have the identifier fixed for each message.


    The real question is what the identifier is to be used for.
    Most of the time, if you need each point of invocation to have a
    separate identifier, it is for some sort of tracing or
    debugging, in which case, __FILE__ and __LINE__ are far more
    useful than anything else you could come up with.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Nov 5, 2008
    #11
  12. mliptak

    James Kanze Guest

    On Nov 4, 3:05 pm, mliptak <> wrote:
    > On Nov 4, 2:53 pm, "Victor Bazarov" <> wrote:
    > > mliptak wrote:
    > > > On Nov 4, 2:21 pm, James Kanze <> wrote:
    > > >> On Nov 4, 11:54 am, mliptak <> wrote:


    > > >>> I'm trying to implement logging in my application, so
    > > >>> that each log message has its unique identifier, e.g.
    > > >>> log(identifier, text)
    > > >>> What I want to achieve is that the compiler screams if
    > > >>> the log() with 'identifier' is also used in some other
    > > >>> place in the code which would make the 'identifier' not
    > > >>> unique. Is that something that can be achieved in C++?


    > > >> It depends on the type of identifier, but in general,
    > > >> yes, provided log is a macro. In the general case, you
    > > >> need a local static variable to achieve it, and the
    > > >> actual identifier won't be known until compile time. If
    > > >> identifier is text, however, you can generate it with a
    > > >> macro using __FILE__ and __LINE__.


    > > > What if I want to set the identifier myself, not have it
    > > > generated at compile time (using local static variable)?


    > > > I was also thinking of using the __FILE__ and __LINE__ but
    > > > the problem is this could change when the code changes,
    > > > and I need to have the identifier fixed for each message.


    > > I think you're still to figure out exactly what you need
    > > (i.e. the "requirements" part of your design). What does it
    > > mean "to have the identifier fixed for each message"? Do
    > > you intend to have the same message sprinkled throughout
    > > your code and have the same identifier accompany it? If the
    > > messages are unique, why nave an identifier at all? If the
    > > messages aren't unique and can be the same, using the exact
    > > location in your code to generate the identifier is a nice
    > > way to ensure uniqueness of them.


    > What I meant by this is to have the "code" associated with
    > particular "text" and this association never changes (which is
    > not true if I'd be using __FILE__ and __LINE__).


    Aha. Something like errno. Or the error codes returned by
    Internet message protocols (smtp, nntp, http...).

    That really requires an external registry. The simplest
    solution is to maintain a list of code/message pairs, use it to
    generate a mapping function, and just pass the code to log
    (which uses the mapping function to obtain the message).

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Nov 5, 2008
    #12
  13. mliptak

    James Kanze Guest

    On Nov 5, 6:49 am, mliptak <> wrote:
    > On Nov 4, 5:15 pm, Bart van Ingen Schenau
    > <> wrote:
    > > On Nov 4, 3:05 pm, mliptak <> wrote:


    > > If you want to have a consistent mapping between a
    > > numeric/symbolic ID and a string, the worst thing you can do
    > > is require the programmer to provide both the ID and the
    > > string.


    > > The solution I would propose is to use only the ID in the
    > > log statements, and use a separate mechanism (for example a
    > > table) to map the ID to a string.
    > > For example, something like this:


    > > enum logID {
    > > code1,
    > > code2,
    > > code4
    > > };


    > > char const * const logString[] = {
    > > "text1",
    > > "text2",
    > > "text5"
    > > };


    > > void log(enum logID id)
    > > {
    > > char const * const text = logString[id];
    > > // write text to log destination}


    > > //usage: log(code1);

    >
    > > To keep the mapping between the ID's and the texts
    > > consistent, you can generate the enumeration and the table
    > > from a single source. This could even be done with the
    > > preprocessor.


    > Yes, I was thinking about this, but the problem is the "text"
    > is not only a text, but string with parameters, either:
    > "text param %d, %s", a, b
    > or:
    > "text param " << a << ", " << b


    > I think I will end up with:
    > void log(enum logID id, ...)
    > {}


    > and do the vsnprintf() magic in there.


    That's a good recepe for program crashes down the line. In such
    cases, the usual solution is for log to return an output stream
    wrapper (or even an ostream&, if you don't have to worry about
    threading), log inserts the header text (the code, plus anything
    you've already mapped), and the client appends the rest.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Nov 5, 2008
    #13
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    1
    Views:
    355
    Roedy Green
    Apr 22, 2008
  2. ToshiBoy
    Replies:
    6
    Views:
    851
    ToshiBoy
    Aug 12, 2008
  3. blackjack612

    Unique Identifiers

    blackjack612, Jul 28, 2009, in forum: C++
    Replies:
    3
    Views:
    377
    James Kanze
    Jul 29, 2009
  4. Replies:
    4
    Views:
    179
    Paul Cooper
    Mar 24, 2006
  5. Token Type
    Replies:
    9
    Views:
    360
    Chris Angelico
    Sep 9, 2012
Loading...

Share This Page