I've been working on this Asynchronous file I/O class for a while now. It compiles fine on it's own, but generates strange linker errors when I try to test the class to verify it works properly. I have no idea why it is generating linker errors, and the functions within the class are defined in it's own .cpp file. I've been at this for a little while now and I'm kinda lost. Hopefully I didn't overcomplicate this class definition. It uses a template and it's defined within it's own namespace. I'll post the code below; and also, if anyone finds this code useful in some way, feel free to use it for whatever you want.

"One objection to a “critique of C#” would be that you can’t talk about C# without talking about the whole “.Net experience”. However, one can approach the topic of Hitler without a complete discussion of Nationalist Socialism, so I feel justified." - Steve White.

Yes, it is because of the template; you can not declare a templated class or function in a header and then define it in a .cpp file.

The whole templated class, declarations and definitions, must be avalible when being used in a translation unit.

Typically this means that you'll either write the whole thing in the header file, or declare in a header file, define in another file (typically with a .inl extension) and then include that file at the end of the header file.

In your project itself you'll include the header file and that is all. You do not have a .cpp file for the definition of the functions.

You're mixing error code genuses (Win errors and COM HRESULT errors). Mixing these only works if you explicitly text against 0 as success/failure conditions, you're using FAILED() which checks for negative values. This will catch E_PENDING and suchlike (since those are negative), but will not catch any error returned from GetLastError() (which aren't). You can transform Windows errors to COM errors with HRESULT_FROM_WIN32(). If you do this, change the return type of your functions to HRESULT for clarity.

Since you're already tied to Windows, consider using something like PathIsRelative or checking whether the second character of szFileName is a colon instead of asking the user to tell you whether it is a full path or not. I'd also explicitly document the lifetime requirements of the szFileName parameter. As it's passed to a different thread, if you do something like this:

This is semantic but WAIT_OBJECT_0 is generally used as to check against the return value to see if the handle is signalled. It works as intended here since it has a value of 0, but it's not terribly usual.

Why thank you all for your replies. Now that makes perfect sense now that I think about it. It looks like that's how the std::vector classes do it. I'm surprised I didn't put 2 and 2 together, especially after working on a project that actually used .inl files for template classes.

Phantom's nailed your immediate problem, but there are a few other things with the code

You're mixing error code genuses (Win errors and COM HRESULT errors). Mixing these only works if you explicitly text against 0 as success/failure conditions, you're using FAILED() which checks for negative values. This will catch E_PENDING and suchlike (since those are negative), but will not catch any error returned from GetLastError() (which aren't). You can transform Windows errors to COM errors with HRESULT_FROM_WIN32(). If you do this, change the return type of your functions to HRESULT for clarity.

Since you're already tied to Windows, consider using something like PathIsRelative or checking whether the second character of szFileName is a colon instead of asking the user to tell you whether it is a full path or not. I'd also explicitly document the lifetime requirements of the szFileName parameter. As it's passed to a different thread, if you do something like this:

This is semantic but WAIT_OBJECT_0 is generally used as to check against the return value to see if the handle is signalled. It works as intended here since it has a value of 0, but it's not terribly usual.

Looks like great advice, but I do have one question. Is CancelIoEx really for Vista and later? If so, then why can I compile that function without errors in Visual Studio 6.0? I'll take your word for it though, since I'm still a bit new to async I/O.

"One objection to a “critique of C#” would be that you can’t talk about C# without talking about the whole “.Net experience”. However, one can approach the topic of Hitler without a complete discussion of Nationalist Socialism, so I feel justified." - Steve White.

"One objection to a “critique of C#” would be that you can’t talk about C# without talking about the whole “.Net experience”. However, one can approach the topic of Hitler without a complete discussion of Nationalist Socialism, so I feel justified." - Steve White.

You're trying to create a thread with a member function pointer as the entrypoint. That won't work.

That's exactly what that error message tells you. Casting away errors is rarely the correct response to a "pointer not convertible" error, and is an EXCELLENT way to introduce very hard to debug errors.

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.ScapeCode - Blog | SlimDX

Using #define on keywords is a good way to break existing code. For example, pretty much any STL header is going to directly or indirectly include a file that uses placement new. If that happens after that kind of #define then lots of code is going to blow up unhappily.