Thread: Static Enums as Parameters

  1. #1
    Registered User
    Join Date
    Jun 2004
    Posts
    8

    Static Enums as Parameters

    Hello all! I have exhausted all forms of searching for my answer here (I have googled, researched MSDN, searched this forum, and asked all my programming buddies if they know how to go about this) and tried many random implementations on my own but I am still having difficulties understanding what I'm supposed to do. I am developing this for use with both Microsoft Visual C++ .Net 2003, and the latest GNU compiler, so it will be cross-platform. (Windows and Linux)

    I am constructing a serialized debug-output class. This debug class has 5 levels of severity: errors, warnings, information, debug, and spam. I would like to have the severity level controlled through the serialization operators using a static enum. Before I post example code I should mention this class follows the Singleton pattern and resides as a local object to the program as a reference, not a pointer. That said, this is an example of how I want this to work:

    Code:
    debugobject << Debug::error << "I am an error!" << endl;
    debugobject << "I am still an error!" << endl;
    debugobject << Debug::debug;
    debugobject << "But now I'm debug!" << endl;
    Here are the snippets of code relevant to my problem inside the class:
    Code:
    public:
    static enum levels { error, warn, info, debug, spam } LEVELS;
    static enum version { anyversion, version1, version2 } VERSIONS;
    
    friend Debug& operator << (Debug &debug, Debug::levels lvl);
    friend Debug& operator << (Debug &debug, Debug::version ver);
    
    private:
    int currentlevel;
    int currentversion;
    The all-capitals "LEVELS" and "VERSIONS" are required to conform to ISO C++ standards... at least according to GNU, though without those it compiles on MSVC with no problem. The above definition compiles on both MSVC and GNU.

    This is the implementation of the operators:

    Code:
    Debug& operator << (Debug &debug, Debug::levels lvl) {
    	debug.currentlevel = lvl;
    	return debug;
    }
    
    Debug& operator << (Debug &debug, Debug::versions ver) {
    	debug.currentversion = ver;
    	return debug;
    }
    This also compiles correctly on both MSVC and GNU. However, trying to actually code a use for this creates 2 different errors on both GNU and MSVC:

    Code:
    debug << (DebugFile::debug) << "Debug!" << endl;
    This is what MSVC says:

    Code:
    test-debug.cpp(21): error C2679: binary '<<' : no operator found which takes a
    right-hand operand of type 'const char [16]' (or there is no acceptable conversion)
    This is what G++ says:

    Code:
    test-debug.cpp:21: error: no match for 'operator<<' in 'operator<<((+debug),
       DebugFile::debug) << "Debug!"'
    DebugFile.h:41: error: candidates are: DebugFile& operator<<(DebugFile&,
       DebugFile::levels)
    DebugFile.h:42: error:                 DebugFile& operator<<(DebugFile&,
       DebugFile::version)
    I've been trying to get this figured out all day and I'm about ready to do something drastic like make an entire seperate class for this sort of thing. I do not wish to have the severity-level-setting parameters passed as strings or ints as I want this debug class to be "solid" and not prone to missing output because it was sent an integer of 1 or a special string to change the severity level of the output.

    Thank you for your help in advance!

  2. #2
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    What you want is (and to add more type-safety):
    Code:
    public:
        enum level_t { error, warn, info, debug, spam };
        enum version_t { anyversion, version1, version2 };
    
        friend Debug& operator << (Debug &debug, Debug::level_t lvl);
        friend Debug& operator << (Debug &debug, Debug::version_t ver);
    
    private:
        level_t currentlevel;
        version_t currentversion;
    The problem is that those are the only overloaded insert operators defined for the Debug class, so things like "Debug!" and endl aren't going to work because they are not of type level_t or version_t.

    gg

  3. #3
    Registered User
    Join Date
    Jun 2004
    Posts
    8
    That is why I said "code snippets". My actual header and implementation is much, much larger to accommadate any and all forms of overloading << you might want.

    So I do not have to have the enum as a static object for me to use it in a non-member operator function? I suppose my assumption there is what led me to run around for a day trying to find a solution.

    The whole purpose of having the currentversion/level as integers is so that I can perform > or < operations on them inside ifs. Or is this not a problem with enum, and debug < spam, spam > error, etc.?
    Last edited by LiquidRain; 06-01-2004 at 06:21 PM.

  4. #4
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    The construct of the code I posted works fine under MSVC and GNU. Using relational operators on enums shouldn't be a problem either.

    If you're still having issues, try putting together a minimal set of code that compiles and demonstrates the issue.
    For example, this works fine under MSVC and GNU:
    Code:
    #include<iostream>
    using namespace std;
    
    class Test
    {
    public:
        enum val_t {VAL1 = 50, VAL2, VAL3};
    
    private:
        val_t m_val;
    
    public:
        Test() : m_val(VAL1) {}
    
        friend Test& operator << (Test &t, const val_t &val) 
        {
            t.m_val = val;
            return t;
        }// Test << val_t
    
        void PrintVal() {cout << (int)m_val << endl;}
    };//Test
    
    int main()
    {
        Test t;
        t << Test::VAL3;
        t.PrintVal();
    
        return 0;
    }//main
    gg

  5. #5
    Registered User
    Join Date
    Jun 2004
    Posts
    8
    Thank you very much for your help!

    I have however one last problem I can't seem to resolve. I am trying now to get my Debug class to be able to handle the endl manipulator but am not having much luck. I hit up the usual resources of this forum, google, and MSDN, and various C++ reference sites, but have had no luck in finding a solution. I imagine it cannot be that complicated, but my puny brain can't seem to get a solution. I believe this is my last roadblock to finishing my class, the rest I can take after this problem. Thanks again!

    [edit] Should add that I have tried creating my own manipulator/overloading endl to handle my Debug class. Both ended in failure.
    Last edited by LiquidRain; 06-01-2004 at 10:36 PM. Reason: Clarification

  6. #6
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    If you want to reuse std::endl, then lookup the type of std::endl and create an overloaded insertion operator of that type. If the passed in operand is std::endl then do whatever.

    gg

  7. #7
    Registered User
    Join Date
    Jun 2004
    Posts
    8
    Ah, I did, however that doesn't work. endl is actually a manipulator function, and as such is not actually a object/type, and cannot actually be passed as a parameter. I have tried creating my own manipulator function but I couldn't get it to work.

  8. #8
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    Code:
    #include<iostream>
    using namespace std;
    
    class Test
    {
    public:
        enum val_t {VAL1 = 50, VAL2, VAL3};
    
    private:
        val_t m_val;
    
    public:
        Test() : m_val(VAL1) {}
    
        friend Test& operator << (Test &t, const val_t &val) 
        {
            t.m_val = val;
            return t;
        }// Test << val_t
    
        Test& operator << (ostream& (__cdecl *manip)(ostream&))
        {
            if (manip == (ostream&(__cdecl*)(ostream&))endl)
                cout << "!endl!" << endl;
            else
                cout << "other" << endl;
            return *this;
        }// operator << manipulator
    
        void PrintVal() {cout << (int)m_val << endl;}
    };//Test
    
    int main()
    {
        Test t;
        t << Test::VAL3;
        t.PrintVal();
    
        t << endl;
    
        return 0;
    }//main
    gg

  9. #9
    Registered User
    Join Date
    Jun 2004
    Posts
    8
    Oof. I have been rightly put in my place as a junior programmer. I certainly would have not gotten that, as I was trying to guess the type of endl from what MSVC had as a tooltip when I hovered over it. If I may ask: how did you obtain the type?

    I had honestly not expected a full solution for any of my questions, so thank you for that, and your patience as I learn.

  10. #10
    Registered User
    Join Date
    Jun 2004
    Posts
    8
    Addendum: The if statement inside your << operator should look like this:

    Code:
    if (manip == (std::basic_ostream<char,std::char_traits<char> >&(*)(std::basic_ostream<char, std::char_traits<char> >&))endl)
    Otherwise it will not compile using GNU.

  11. #11
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    The only difference between that and what I have is that you've ommitted "__cdecl".

    I got the signature for endl from MSVC's implementation, which included the calling convetion.

    MinGW's port of GCC supports the __cdecl keyword by auto-magically converting it to attribute syntax: __attribute__((cdecl)), which GCC understands.

    Calling conventions are compiler specific and should be left out for maximum portability.

    Here's a better version:
    Code:
        typedef std::ostream& (*manip_fn)(std::ostream&);
        Test& operator << (manip_fn manip)
        {
            if (manip == (manip_fn)endl)
                cout << "!endl!" << endl;
            else
                cout << "other" << endl;
            return *this;
        }// operator << manipulator
    Use the std::ostream typedef. No point in typing out the full template (unless you like typing).

    gg

  12. #12
    Registered User
    Join Date
    Jun 2004
    Posts
    8
    I see. I wasn't quite sure as I just copied and pasted the type that GNU gave as the assumed conversion for endl, added some brackets, and was quite delighted when it compiled in both and worked. =)

    The class is working beautifully now, thank you for everything!

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Seg Fault in Compare Function
    By tytelizgal in forum C Programming
    Replies: 1
    Last Post: 10-25-2008, 03:06 PM
  2. seg fault at vectornew
    By tytelizgal in forum C Programming
    Replies: 2
    Last Post: 10-25-2008, 01:22 PM
  3. uploading file to http server via multipart form data
    By Dynamo in forum C++ Programming
    Replies: 1
    Last Post: 09-03-2008, 04:36 AM
  4. LNK2001 ERROR!!! need help
    By lifeafterdeath in forum C++ Programming
    Replies: 7
    Last Post: 05-27-2008, 05:05 PM
  5. get keyboard and mouse events
    By ratte in forum Linux Programming
    Replies: 10
    Last Post: 11-17-2007, 05:42 PM