1

I am trying to create a very modular program by using interfaces. Coming from a C# background, I would use interfaces as variable types so I could use polymorphism, allowing myself/others to pass many different objects that inherit from this interface into a function/variable. However, I am getting many strange errors when trying to do this in C++. What am I doing wrong here?

I would like be able to have interface-typed variables. However, the following produces compilation errors. I think the compiler thinks my ErrorLogger class is abstract, because it inherits from an abstract class or something.

ILogger * errorLogger = ErrorLogger();

error C2440: 'initializing' : cannot convert from 'automation::ErrorLogger' to 'automation::ILogger *'

If I am going about this the wrong way, even design-wise, I am learning and will gladly listen to any and all advice.

ILogger.h:

#ifndef _ILOGGER_H_
#define _ILOGGER_H_

namespace automation
{
    class ILogger
    {
    public:
        virtual void Log(const IError &error) = 0;
    };
}
#endif

ErrorLogger.h:

#ifndef _ERRORLOGGER_H_
#define _ERRORLOGGER_H_
#include "ILogger.h"
#include "IError.h"

/* Writes unhandled errors to a memory-mapped file.
 * 
**/

namespace automation
{
    class ErrorLogger : public ILogger
    {
    public:
        ErrorLogger(const wchar_t * file = nullptr, const FILE * stream = nullptr);
        ~ErrorLogger(void);
        void Log(const IError &error);
    };
}
#endif

ErrorLogger.cpp:

#include "stdafx.h"
#include "ErrorLogger.h"
#include "IError.h"

using namespace automation;

ErrorLogger::ErrorLogger(const wchar_t * file, const FILE * stream)
{

}

void ErrorLogger::Log(const IError &error)
{
    wprintf_s(L"ILogger->ErrorLogger.Log()");
}

ErrorLogger::~ErrorLogger(void)
{
}

IError.h:

#ifndef _IERROR_H_
#define _IERROR_H_

namespace automation
{
    class IError
    {
    public:
        virtual const wchar_t *GetErrorMessage() = 0;
        virtual const int &GetLineNumber() = 0;
    };
}
#endif

Compilation Errors:

enter image description here

Thanks, -Francisco

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
Francisco Aguilera
  • 3,099
  • 6
  • 31
  • 57
  • In C++, polymorphism requires using pointers (possibly smart pointers) or references. – Andy Prowl Jul 12 '13 at 23:02
  • 2
    The compilation errors are unreadably small. Can you put them in as plain text instead of a picture? – PherricOxide Jul 12 '13 at 23:03
  • 2
    Please post actual code instead of images. – Captain Obvlious Jul 12 '13 at 23:05
  • @CaptainObvlious: Well, even if your classes are concrete and you do `Derived d; Base b = d`, what you get is slicing. So yes, in this case the problem is also with `Base` being abstract, but that's only half of the story ;) – Andy Prowl Jul 12 '13 at 23:09
  • As @CaptainObvlious notes, actual code in text form is helpful. The reason is that we can pick it up with our mice, paste it into a test \*.cpp file, and try it ourselves before answering. – thb Jul 12 '13 at 23:10
  • @AndyProwl Ah yeah wasn't even thinking about slicing ;) – Captain Obvlious Jul 12 '13 at 23:11
  • A general observation: you should not have written this much code before testing it. – Beta Jul 12 '13 at 23:15
  • @Beta I wrote the code, and then made a design decision to make an error a general interface rather than the COM HRESULTs I was dealing with, for the portability of the code. Adding this IError in broke it. :) – Francisco Aguilera Jul 12 '13 at 23:18
  • Your include guard token is a [reserved identifier](http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier). – chris Jul 12 '13 at 23:27
  • Thank, @chris I changed all my include guards to remove the initial underscore. – Francisco Aguilera Jul 12 '13 at 23:35

2 Answers2

3

ILogger * errorLogger = ErrorLogger(); errorLogger is a pointer, you need to initialize it with new operator.

The correct way of defining base pointer which points to derieved class is:

automation::ILogger * errorLogger = new automation::ErrorLogger();
//                                  ^^^^

Better use smart pointers in modern C++:

#include <memory>
std::unique_ptr<automation::ILogger> errorLoggerPtr(new automation::ErrorLogger());

Also you need to include IError.h in ILogger.h

#include "IError.h"

Other suggestions:

1 use fstream isntead of FILE 2 use std::wstring instead of wchar_t * 2 in cpp file, dont' call

using namespace automation;

instead wrap function definitions with namespace, like what you did in header file:

namespace automation
{
    ErrorLogger::ErrorLogger(const std::wstring& file, std::ofstream& stream)
    {
    }
}

The point is don't mix C++ code with C code, C++ classes like string, fstream provides RAII, it's safer and easier to use.

billz
  • 44,644
  • 9
  • 83
  • 100
  • Thank you @billz. While this does work, does this then mean that I cannot initialize an ILogger pointer to an ErrorLogger in the stack? Because the new keyword allocates it in the heap does it not? – Francisco Aguilera Jul 12 '13 at 23:36
  • did you "#include "IError.h" in ILogger.h? – billz Jul 12 '13 at 23:38
  • I would argue that using a smart pointer is the correct way. They can be used polymorphically. – chris Jul 12 '13 at 23:50
  • Thanks for the recommendations, the reason I wasn't using smart pointers is for code portability reasons, same reason I am not using #pragma, etc. As long as there is still a decently easy way to be able to port the code to GC++ I would consider using all of these additions. – Francisco Aguilera Jul 13 '13 at 01:01
0

You need to #include "IError.h" in the ILogger.h header file.

user2093113
  • 3,230
  • 1
  • 14
  • 21
  • Ah, well that was silly of me. How about my other question? "be able to have interface-typed variables. However, the following also produces compilation errors. I think the compiler thinks my ErrorLogger class is abstract, because it inherits from an abstract class or something. ILogger logger = ErrorLogger(); If I am going about this the wrong way, even design-wise, I am learning and will gladly listen to any and all advice." – Francisco Aguilera Jul 12 '13 at 23:15