2

I'm a newer of using C++ template and I got trouble with template compiling. I want to write a similar factory method with template but compiler error says that 'ip is not the member of _FileWriterInfo'. I was confused because it has be defined in NetWriterInfo struct but not in FileWriterInfo. And if I cancel the 'ip' member defination, compiler works. Apparently T param of NetWriter may infer to FileWriterInfo struct by mistake. How can I get rid of it? plz help.

#include <iostream>
#include <string>

enum WriterFormat
{
    WTYPE_FILE = 0,
    WTYPE_NET = 1
};

typedef struct _FileWriterInfo
{
    std::string name;
    std::string page;
}FileWriterInfo;

typedef struct _NetWriterInfo
{
    std::string name;
    std::string ip;
}NetWriterInfo;

template<typename T>
class Writer
{
public:
    virtual ~Writer() {}
    virtual std::string Write(T info) = 0;
};

template<typename T>
class FileWriter : public Writer<T>
{
public:
    std::string Write(T info) override {
        std::cout << "name:" << info.name << "\n";
        std::cout << "page:" << info.page << "\n";
        return info.name;
    }
};

template<typename T>
class NetWriter : public Writer<T>
{
public:
    std::string Write(T info) override {
        std::cout << "name:" << info.name << "\n";
        std::cout << "ip:" << info.ip << "\n";
        return info.name;
    }
};

class Creator
{
    Creator() {};
public:
    template<typename T>
    static Writer<T>* CreateWriter(WriterFormat fmt)
    {
        Writer<T>* p = nullptr;
        if (fmt == WTYPE_FILE)
            p = new FileWriter<T>;
        if (fmt == WTYPE_NET)
            p = new NetWriter<T>;
        return p;
    }
};

void WriteFile()
{
    FileWriterInfo info = { "Hello","100" };
    Writer<FileWriterInfo>* w = Creator::CreateWriter<FileWriterInfo>(WTYPE_FILE);
    w->Write(info);
    return;
}

int main()
{
    WriteFile();
    return 0;
}
NickWuzh
  • 23
  • 5
  • 2
    `FileWriterInfo` has the members `name` and `page`, no `ip`. – mch Jun 13 '22 at 08:33
  • Maybe, you try to print the value of member `ip` without checking the type of the template. So the compiler tries to substitute `FileWriterInfo` in the template type and fails. – kiner_shah Jun 13 '22 at 08:34
  • 1
    The problem is with line `p = new NetWriter;` when `T == FileWriterInfo` it will create the classes out of the class templates and that results in the compile time error. – mch Jun 13 '22 at 08:39
  • @mch actually I want to call the FileWriter method with FileWriterInfo struct param, but it seems to infer to the NetWriterInfo. – NickWuzh Jun 13 '22 at 08:41
  • Reopened. I didn't think the duplicate was a good answer to this question (which is quite subtle). – john Jun 13 '22 at 08:43
  • 3
    identifiers starting with underscore followed by a capital letter are reserved. You should not use them – 463035818_is_not_an_ai Jun 13 '22 at 08:51
  • 2
    `typedef struct _FileWriterInfo {/*...*/} FileWriterInfo;` is also not how to do it in C++. This has no benefits (if at all maybe some problems in edge cases) over just `struct FileWriterInfo {/*...*/};`. The `typedef struct` construct is used in C, not C++. – user17732522 Jun 13 '22 at 09:30

1 Answers1

2

The CreateWriter function instantiates the FileWriter and NetWriter classes with the FileWriterInfo structure. Accordingly, the compiler tries to instantiate the NetWriter::Write function with the type FileWriterInfo, and we get an error.

You can place Write methods directly to FileWriterInfo and NetWriterInfo stuctures (according to the principle of data encapsulation). It also can simplify the code.

Elmir
  • 110
  • 11
  • how can i modify code if I want to use T with different XXXWriterInfo and got the compile works. – NickWuzh Jun 13 '22 at 08:53
  • Can we refactor your code, or need keep this classes structure? – Elmir Jun 13 '22 at 09:11
  • please refactor freely, and I think that I know how to make it works without using template but passing `void*` param and `static_cast` it to the right struct. But I still want to know how to fix the problem. – NickWuzh Jun 13 '22 at 09:18
  • 1
    I placed `Write` directly on the structures to be output. Thus, we no longer need the `FileWriter` and `NetWriter` classes, and we managed to simplify the `CreateWriter` method. https://pastebin.com/M2ftiGkS – Elmir Jun 13 '22 at 09:19
  • I never thought about that. thx for your help! – NickWuzh Jun 13 '22 at 09:28
  • @NickWuzh: I think part of your confusion might be because C++ is a compiled language. The `if (fmt == WTYPE_NET)` check happens at runtime, after everything including `NetWriter` is compiled. In script languages, code that is not executed is generally not even checked for errors, but that's a side effect of how those interpreters work. – MSalters Jun 13 '22 at 09:40
  • @MSalters I thought that compiler can auto infer `T == NetWriterInfo` in `NetWriter` but actually not. so I'm confused if the compiler infer 'T == FileWriter' at first and it cannot infer to another class – NickWuzh Jun 13 '22 at 09:52
  • 1
    @NickWuzh: The search term you're looking for is "deduce" - compilers can sometimes deduce template parameters. For function templates, the compiler can deduce the template parameters from function arguments. For class templates, the compiler can deduce them from the constructor arguments. But the compiler won't deduce parameters that you explicitly prvide, as in `CreateWriter`. – MSalters Jun 13 '22 at 10:57