1

I'm trying to make a logger which logs to std::cout and to a file. This is my class:

.h file:

class ProblemsManager {
        (...)
private:
        (...)

    class logger {
    private: 
        std::ofstream fileStream;
        static const std::string LOG_PATH;
    public: 
        logger();
        ~logger();


    template<class T> 
    friend logger & operator<<(logger &log, const T & bytes) {
        log.fileStream<<bytes;
        std::cout<<bytes;
        return log;
    }
    };
};

.cpp file

(...)
const std::string ProblemsManager::logger::LOG_PATH = "F:\\Dropbox\\workspace - Visual Studio\\PEuler\\PEuler\\PEuler.log";

ProblemsManager::logger::logger() : fileStream(LOG_PATH,std::ofstream::out) {}
ProblemsManager::logger::~logger() {}

Then if I try to do:

ProblemsManager::logger log;
log<<"test";

I get:

1>f:\dropbox\workspace - visual studio\peuler\peuler\problemsmanager.cpp(47): error C3767: '<<': candidate function(s) not accessible 1> could be the friend function at 'f:\dropbox\workspace - visual studio\peuler\peuler\problemsmanager.h(37)' : '<<' [may be found via argument-dependent lookup]

José D.
  • 4,175
  • 7
  • 28
  • 47
  • It's interesting that your template functions have their definitions separated from their declarations; I thought this wasn't possible because every file that needs to use the templated definitions can't generate them from just the declaration (since the definitions themselves are in a wholly different cpp file). I'm surprised it even compiles o.O – Suedocode Aug 01 '13 at 21:11
  • Is the `logger` type private to the enclosing type `ProblemsManager`? – David Rodríguez - dribeas Aug 01 '13 at 21:13
  • @Aggieboy: It does not compile (that is why he is asking), but what you are concerned with would be a linker error. – David Rodríguez - dribeas Aug 01 '13 at 21:14
  • @DavidRodríguez-dribeas Aha I see! I was confused as to why *that* wasn't the compiler error, but it makes sense that it would be a linker error afterwards. – Suedocode Aug 01 '13 at 21:16
  • Removing one of the overloads does not fix the problem. Sorry what would be a 'literal string'? I though log<<"test"; was that – José D. Aug 01 '13 at 21:18
  • possible duplicate of [Why can templates only be implemented in the header file?](http://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file) – Mooing Duck Aug 01 '13 at 21:21
  • @MooingDuck: That is only part of the problem, and not necessarily the one the compiler is complaining about. – David Rodríguez - dribeas Aug 01 '13 at 21:22
  • I have edited the question and it is now implemented in the header file. – José D. Aug 01 '13 at 21:29

3 Answers3

3

There are a couple of issues with your templates, the first is that both differ only on the reference, and that will cause issues. You only want one (that reads and does not write to the argument):

    template<class T> 
    friend logger & operator<<(logger& log, const T & bytes);
    //                                      ^^^^^

Now the second problem is that the templates should be defined somewhere where the compiler can see it when generating the instantiation. That basically means that the definition must be in the header, not in the .cpp file. [*]

Beyond that, since the logger type is private to ProblemsManager, you cannot access it from the namespace level, so you will have issues defining the free function, since it cannot access the nested type. The options would be making the type public, making the function also a friend of ProblemsManager, or as I would recommend, just define the template inline inside the class definition:

class ProblemsManager {
private:
    class logger {
        template<class T> 
        friend logger & operator<<(logger& log, T & bytes) {
           // implementation goes here
        } 
    };
};

[*] This might actually be the exception to the rule, since being a private type, I can only assume that all uses of the logger and thus the operator<< will happen within the translation unit that defines ProblemsManager members. If that is the case, you can disregard this paragraph.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • That seems to fix that problem. But now I get 1>f:\dropbox\workspace - visual studio\peuler\peuler\problemsmanager.cpp(47): error C3767: '<<': candidate function(s) not accessible 1> could be the friend function at 'f:\dropbox\workspace - visual studio\peuler\peuler\problemsmanager.h(37)' : '<<' [may be found via argument-dependent lookup] – José D. Aug 01 '13 at 21:24
  • I have edited the answer showing how my code is at the moment – José D. Aug 01 '13 at 21:25
  • 1
    @Trollkemada with your latest code the only problem is that `logger` is private. Making it public it compiles fine for me. – jrok Aug 01 '13 at 21:29
  • To say something briefly about "exception" part, this is still a bad idea. The class header file defines interface details; public functions are used by users, and both private and public functions are defined in cpp files (not necessarily just one). Cpp files that are implementing private functions will use other private functions from the header file as an interface. *The template functions can not be used in this way* since only the cpp file that defines them can actually use them. – Suedocode Aug 01 '13 at 21:32
  • @jrok: Regarding the comment on the deleted (as it was incomplete) answer, there is no problem with overloading just on value/reference, *but* the overloads will cause ambiguity when the call is an lvalue, which is hardly what you want. – David Rodríguez - dribeas Aug 01 '13 at 21:33
  • @Aggieboy: I would actually go the opposite way. The whole `logger` type is an implementation detail and should not be in the header. Once that type is not in the header, it becomes obvious that the template should not be in the header either. Those are *implementation details*, and if your code structure requires it, you might be forced to push that to the header (and bother everyone else with your details), but if you follow the common convention of a single .cpp for a type (`ProblemsManager`) then there is no need to bother anyone with your details – David Rodríguez - dribeas Aug 01 '13 at 21:34
0

As operator<< functions are not members but friends you cannot use 'this' inside them. You probably want to return 'log' there and appropriately qualify log members with 'log.'.

Tomek
  • 4,554
  • 1
  • 19
  • 19
0

I'll give it a long shot.

log<<"test";

The above call is a call to operator<< function with the first parameter of logger & and a second of const char[5].

The first overload probably cannot bind as you are overloading on reference and passing const object. The second fails as you are passing array by value and you cannot do that. I am just not sure why your array in not collapsing to a pointer.

Have you tried to overload on const reference? And by the way - why do you overload on value and reference?

Tomek
  • 4,554
  • 1
  • 19
  • 19
  • I have edited my question. (Now I am using just an overload on const reference, as you suggested) – José D. Aug 01 '13 at 21:27
  • You still have your logger private. I am surprised that compiler is not complaining about it when you define log variable. – Tomek Aug 01 '13 at 21:31