dllexport'ing an incomplete class - visual-c++

[EDIT: further digging revealed a different root issue. I'm rephrasing the question, but leaving the old version below, for consistency with the answer by #Leo]
It seems VC++ (both under VS2005 & VS2010) allows me to dllexport classes with missing implementations! The following code builds fine:
// missingimp.h :
class __declspec(dllexport) MissingImp
{
void DoStuff(); // no implementation anywhere
void DoMoreStuff(); // neither for this
}
// missingimp.cpp
#include "missingimp.h"
The code both compiles and links fine (in dll configuration) - and of course statically linking with the resulting dll fails.
Is this be a bug? is this behavior somehow by design??
[Old question:]
I'm trying to dllexport a class, that has a data member templated on a forward-declared type:
// Class2Export.h:
class ForwardDeclared ;
template<class T> class TemplatedClass
{
T *m_ptr;
public:
TemplatedClass() { m_ptr->DoSomething(); }
};
class __declspec(dllexport) ExportedClass
{
TemplatedClass<ForwardDeclared> TemplateForward;
};
// Class2Export.cpp:
#include "Class2Export.h"
(This is not a contrived example - in the real code TemplatedClass is a smart pointer, but that seems irrelevant to the issue at hand.)
This code fails to compile, with -
error C2027: use of undefined type
'ForwardDeclared'
That still makes some kind of sense, based on a reply from MS:
If TemplatedClass has a constructor
then a constructor will be
automatically generated for
ExportedClass. Since ExportedClass is
exported the compiler tries to export
the constructor but fails to generate
code for it because ForwardDeclared is
unknown.
But I suspect that is not the ultimate answer, as when I declare (without even implementing!) a ctor for ExportedClass:
...
class __declspec(dllexport) ExportedClass
{
ExportedClass();
TemplatedClass<ForwardDeclared> TemplateForward;
};
...
both compile and link succeed (with a due warning*). The issue over at MS-connect seems abandoned - perhaps anyone can shed some light over this strange behaviour?
Thanks!
*EDIT: the generated warning is C4251 :
class 'TemplatedClass ' needs to have dll-interface to be used by clients of class 'ExportedClass'

Unless I've got the wrong end of the stick, the issue is about when the ExportedClass constructor code is generated.
If the ExportedClass constructor code is generated before ForwardDeclared is declared (not forward-declared but properly declared) then you'll get an error, since the ExportedClass constructor implicitly calls the TemplatedClass constructor, and that calls a method on ForwardDeclared which is undefined.
Your final example, the one which compiles and links with a warning, works because the ExportedClass constructor is never defined. (Presumably the warning is that ExportedClass::ExportedClass does not exist. Since nothing actually tries to use it it's just a warning and not an error.) You've avoided the issue there. As it is, that code is not useful as nothing can create ExportedClass (it has no constructor) but if you define the constructor somewhere then everything should work fine, provided ForwardDeclared is declared/defined before then.
If you change your final example to this you should get the errors back again: (All that's added are two braces to give the ExportedClass constructor an empty body)
class __declspec(dllexport) ExportedClass
{
ExportedClass() { } // Error here
TemplatedClass<ForwardDeclared> TemplateForward;
};
And that code is what you are implicitly doing if you don't have a constructor for ExportedClass at all. In these cases the constructor's code is being generated there and then in the header file. On the other hand, when the constructor is declared without a body in the header you are leaving the code to be defined and generated somewhere else.

Eventually I found an answer in MS forums.
I'm linking to it here, in case it's useful to anyone some day.

Related

Is it OK to define an opaque type as an empty struct to get around linker warning in C++/CLI?

I have a C++ project that is configured to use CLR. The project contains a subclass of CTreeCtrl, i.e. a class provided by Microsoft that I have no control over. Since the public interface of CTreeCtrl heavily uses the type HTREEITEM, it is unavoidable that the subclass also makes use of the type - but since the type is "opaque", the subclass only passes around HTREEITEM references without actually doing anything with the referenced objects.
By "opaque", I mean that HTREEITEM is only visible as a forward-declared type. Here's the declaration that I see in CommCtrl.h:
struct _TREEITEM;
typedef struct _TREEITEM *HTREEITEM;
Unfortunately, in a release build this usage of HTREEITEM generates the following linker warning:
foo.obj : warning LNK4248: unresolved typeref token (01000017) for '_TREEITEM'; image may not run
Here is the MSDN link for the warning. I have searched the net for a solution to get rid of the warning, but have found nothing - only confirmation that over the years other people have encountered this warning in relation to HTREEITEM as well.
Now I have been thinking of a workaround myself. Given that
My class only gets HTREEITEM references from CtreeCtrl and passes them back to CtreeCtrl, without any kind of interpretation or function calls
HTREEITEM is merely a pointer to _TREEITEM
It follows that all that my class ever does is pass around pointers to _TREEITEM. My idea for a workaround therefore is this: Why not define _TREEITEM as an empty struct in my project, like this:
struct _TREEITEM
{
};
Obviously this makes the linker happy since it now sees the complete type. The fact that the struct definition is incorrect is not relevant since my class is only passing around pointers to _TREEITEM, i.e. all that the compiler needs to know is the size of a HTREEITEM.
I have tried this workaround and it seems to work, not only at compile time but at runtime as well. So what do you think of this workaround? Did I overlook something? I certainly won't be offended if you call it an ugly hack :-)
FWIW: I am currently on Visual Studio 2010.

Passing managed reference (this) to unmanaged code and call managed callback

this is the source of the issue. My answer there was deleted with the hint to start a new question. So, here we go:
I want to pass the managed reference of this to unmanaged code. And then call the managed callback out of the unmanaged callback.
public ref class CReader
with a private field
private:
[...]
void *m_pTag;
[...]
In the constructor of the managed class I initialize the m_pTag like this:
m_pTag = new gcroot<CReader ^>(this);
Later, I pass this void *m_pTag to the unmanaged code. If the unmanaged callback is called, I'm casting the void *m_pTag back to managed reference and call the managed callback
(*(gcroot<CReader ^>*)pTag)->MANAGEDCALLBACKFUNCTION
and there is an exception thrown, if the DLL is used under another AppDomain. The Debugger stops in gcroot.h on line
// don't return T& here because & to gc pointer not yet implemented
// (T should be a pointer anyway).
T operator->() const {
// gcroot is typesafe, so use static_cast
return static_cast<T>(__VOIDPTR_TO_GCHANDLE(_handle).Target);
}
with Cannot pass a GCHandle across AppDomains.
My question is, what should I do?
Sincerly,
Sebastian
==================== EDIT ====================
I am now able to reproduce the problem. I've took some screenshots, to show the issue.
1st screenshot: constructor
snd screenshot: callback
The problem is, that the value-member of the struct gcroot in the callback is empty.
Thank you.
Sebastian
==================== EDIT ====================
Push.
The gcroot<> C++ class is a wrapper that uses the GCHandle class. The constructor calls GCHandle.ToIntPtr() to turn the handle into an opaque pointer, one that you can safely store as a member of an unmanaged struct or C++ class.
The cast then, later, converts that raw pointer back to the handle with the GCHandle.FromIntPtr() method. The GCHandle.Target property gives you the managed object reference back.
GCHandle.FromIntPtr() can indeed fail, the generic exception message is "Cannot pass a GCHandle across AppDomains". This message only fingers the common reason that this fails, it assumes that GCHandle is used in safe code and simply used incorrectly.
The message does not cover the most common reason that this fails in unsafe code. Code that invariably dies with undiagnosable exceptions due to heap corruption. In other words, the gcroot<> member of the C++ class or struct getting overwritten with an arbitrary value. Which of course dooms GCHandle.FromIntPtr(), the CLR can no longer find the handle back from a junk pointer value.
You diagnose this bug the way you diagnose any heap corruption problem. You first make sure that you get a good repro so you can trip the exception reliably. And you set a data breakpoint on the gcroot member. The debugger automatically breaks when the member is written inappropriately, the call stack gives you good idea why this happened.

Simple singleton pattern - Visual C++ assertion failure

I'm recently programming a very simple logger class in Visual C++ 2010, but I have a problem. Everytime I run the program, a debug assertion failure appears.
Expression: _CrtIsValidHeapPointer(pUserData)
This is how my class looks like (basically it's only a little modified from an answer here C++ Singleton design pattern):
class Logger
{
public:
// Returns static instance of Logger.
static Logger& getInstance()
{
static Logger logger; // This is where the assertion raises.
return logger;
}
void logError(std::string errorText);
// Destructor.
~Logger();
private:
std::ofstream logFileStream;
// The constructor is private to prevent class instantiating.
Logger();
// The copy constructor and '=' operator need to be disabled.
Logger(Logger const&) { };
Logger& operator=(Logger other) { };
};
And the constructor is:
Logger::Logger()
: logFileStream(Globals::logFileName, std::ios_base::trunc)
{
// (Tries to open the file specified in Globals for (re)writing.)
}
I found out that I can solve it by using static variables or methods somehow, but I don't understand what's wrong with this code. Does anyone know, where the problem is?
EDIT: FYI, the failure raises when this code is called (for the first time):
Logger::getInstance().logError("hello");
EDIT 2: This is the definition of logFileName in Globals:
static const std::string logFileName = "errorLog.log";
My guess is that you're calling getInstance() from the constructor of another global variable, and encountering the infamous initialisation order fiasco - it's unspecified whether or not Globals::logFileName has been initialised before any globals in other translation units.
One fix is to use an old-school C string, which will be statically initialised before any global constructor is called:
static const char * logFileName = "errorLog.log";
Another possibility is to access it via a function:
static std::string logFileName() {return "errorLog.log";}
My favoured solution would be to remove the global instance altogether, and pass a reference to whatever needs it; but some might find that rather tedious, especially if you already have a large amount of code that uses the global.
C++/CLI is not standard C++ and plays by slightly different rules. Are you using C++/CLI managed code at all? (/clr compiler option?) This looks like a common problem when mixing C++ (unmanaged) code with C++/CLI (managed) code. It has to do with the way managed and unmanaged construction and destruction happens at program initialization and program exit. Removing the destructor works for me - can you do that in your Logger class?
For more details and possible workarounds:
http://www.codeproject.com/Articles/442784/Best-gotchas-of-Cplusplus-CLI
http://social.msdn.microsoft.com/Forums/vstudio/en-US/fa0e9340-619a-4b07-a86b-894358d415f6/crtisvalidheappointer-fails-on-globally-created-object-within-a-static-llibrary?forum=vcgeneral
http://forums.codeguru.com/showthread.php?534537-Memory-leaks-when-mixing-managed-amp-native-code&p=2105565

Possible C# 4.0 compiler error, can others verify?

Since I don't know exactly what part of it alone that triggers the error, I'm not entirely sure how to better label it.
This question is a by-product of the SO question c# code seems to get optimized in an invalid way such that an object value becomes null, which I attempted to help Gary with yesterday evening. He was the one that found out that there was a problem, I've just reduced the problem to a simpler project, and want verification before I go further with it, hence this question here.
I'll post a note on Microsoft Connect if others can verify that they too get this problem, and of course I hope that either Jon, Mads or Eric will take a look at it as well :)
It involves:
3 projects, 2 of which are class libraries, one of which is a console program (this last one isn't needed to reproduce the problem, but just executing this shows the problem, whereas you need to use reflector and look at the compiled code if you don't add it)
Incomplete references and type inference
Generics
The code is available here: code repository.
I'll post a description below of how to make the projects if you rather want to get your hands dirty.
The problem exhibits itself by producing an invalid cast in a method call, before returning a simple generic list, casting it to something strange before returning it. The original code ended up with a cast to a boolean, yes, a boolean. The compiler added a cast from a List<SomeEntityObject> to a boolean, before returning the result, and the method signature said that it would return a List<SomeEntityObject>. This in turn leads to odd problems at runtime, everything from the result of the method call being considered "optimized away" (the original question), or a crash with either BadImageFormatException or InvalidProgramException or one of the similar exceptions.
During my work to reproduce this, I've seen a cast to void[], and the current version of my code now casts to a TypedReference. In one case, Reflector crashes so most likely the code was beyond hope in that case. Your mileage might vary.
Here's what to do to reproduce it:
Note: There is likely that there are more minimal forms that will reproduce the problem, but moving all the code to just one project made it go away. Removing the generics from the classes also makes the problem go away. The code below reproduces the problem each time for me, so I'm leaving it as is.
I apologize for the escaped html characters in the code below, this is Markdown playing a trick on me, if anyone knows how I can rectify it, please let me know, or just edit the question
Create a new Visual Studio 2010 solution containing a console application, for .NET 4.0
Add two new projects, both class libraries, also .NET 4.0 (I'm going to assume they're named ClassLibrary1 and ClassLibrary2)
Adjust all the projects to use the full .NET 4.0 runtime, not just the client profile
Add a reference in the console project to ClassLibrary2
Add a reference in ClassLibrary2 to ClassLibrary 1
Remove the two Class1.cs files that was added by default to the class libraries
In ClassLibrary1, add a reference to System.Runtime.Caching
Add a new file to ClassLibrary1, call it DummyCache.cs, and paste in the following code:
using System;
using System.Collections.Generic;
using System.Runtime.Caching;
namespace ClassLibrary1
{
public class DummyCache<TModel> where TModel : new()
{
public void TriggerMethod<T>()
{
}
// Try commenting this out, note that it is never called!
public void TriggerMethod<T>(T value, CacheItemPolicy policy)
{
}
public CacheItemPolicy GetDefaultCacheItemPolicy()
{
return null;
}
public CacheItemPolicy GetDefaultCacheItemPolicy(IEnumerable<string> dependentKeys, bool createInsertDependency = false)
{
return null;
}
}
}
Add a new file to ClassLibrary2, call it Dummy.cs and paste in the following code:
using System;
using System.Collections.Generic;
using ClassLibrary1;
namespace ClassLibrary2
{
public class Dummy
{
private DummyCache<Dummy> Cache { get; set; }
public void TryCommentingMeOut()
{
Cache.TriggerMethod<Dummy>();
}
public List<Dummy> GetDummies()
{
var policy = Cache.GetDefaultCacheItemPolicy();
return new List<Dummy>();
}
}
}
Paste in the following code in Program.cs in the console project:
using System;
using System.Collections.Generic;
using ClassLibrary2;
namespace ConsoleApplication23
{
class Program
{
static void Main(string[] args)
{
Dummy dummy = new Dummy();
// This will crash with InvalidProgramException
// or BadImageFormatException, or a similar exception
List<Dummy> dummies = dummy.GetDummies();
}
}
}
Build, and ensure there are no compiler errors
Now try running the program. This should crash with one of the more horrible exceptions. I've seen both InvalidProgramException and BadImageFormatException, depending on what the cast ended up as
Look at the generated code of Dummy.GetDummies in Reflector. The source code looks like this:
public List<Dummy> GetDummies()
{
var policy = Cache.GetDefaultCacheItemPolicy();
return new List<Dummy>();
}
however reflector says (for me, it might differ in which cast it chose for you, and in one case Reflector even crashed):
public List<Dummy> GetDummies()
{
List<Dummy> policy = (List<Dummy>)this.Cache.GetDefaultCacheItemPolicy();
TypedReference CS$1$0000 = (TypedReference) new List<Dummy>();
return (List<Dummy>) CS$1$0000;
}
Now, here's a couple of odd things, the above crash/invalid code aside:
Library2, which has Dummy.GetDummies, performs a call to get the default cache policy on the class from Library1. It uses type inference var policy = ..., and the result is an CacheItemPolicy object (null in the code, but type is important).
However, ClassLibrary2 does not have a reference to System.Runtime.Caching, so it should not compile.
And indeed, if you comment out the method in Dummy that is named TryCommentingMeOut, you get:
The type 'System.Runtime.Caching.CacheItemPolicy' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Runtime.Caching, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.
Why having this method present makes the compiler happy I don't know, and I don't even know if this is linked to the current problem or not. Perhaps it is a second bug.
There is a similar method in DummyCache, if you restore the method in Dummy, so that the code again compiles, and then comment out the method in DummyCache that has the "Try commenting this out" comment above it, you get the same compiler error
OK, I downloaded your code and can confirm the problem as described.
I have not done any extensive tinkering with this, but when I run & reflector a Release build all seems OK (= null ref exception and clean disassembly).
Reflector (6.10.11) crashed on the Debug builds.
One more experiment: I wondered about the use of CacheItemPolicies so I replaced it with my own MyCacheItemPolicy (in a 3rd classlib) and the same BadImageFormat exception pops up.
The exception mentions : {"Bad binary signature. (Exception from HRESULT: 0x80131192)"}

Visual C++ doesn't see operator<< overload

I have a vector class that I want to be able to input/output from a QTextStream object. The forward declaration of my vector class is:
namespace util {
template <size_t dim, typename T>
class Vector;
}
I define the operator<< as:
namespace util {
template <size_t dim, typename T>
QTextStream& operator<<(QTextStream& out, const util::Vector<dim,T>& vec)
{
...
}
template <size_t dim, typename T>
QTextStream& operator>>(QTextStream& in,util::Vector<dim,T>& vec)
{
..
}
}
However, if I ty to use these operators, Visual C++ returns this error:
error C2678: binary '<<' : no operator found which takes a left-hand operand of type 'QTextStream' (or there is no acceptable conversion)
A few things I tried:
Originaly, the methods were defined as friends of the template, and it is working fine this way with g++.
The methods have been moved outside the namespace util
I changed the definition of the templates to fit what I found on various Visual C++ websites.
The original friend declaration is:
friend QTextStream& operator>>(QTextStream& ss, Vector& in) { ... }
The "Visual C++ adapted" version is:
friend QTextStream& operator>> <dim,T>(QTextStream& ss, Vector<dim,T>& in);
with the function pre-declared before the class and implemented after. I checked the file is correctly included using:
#pragma message ("Including vector header")
And everything seems fine. Doesn anyone has any idea what might be wrong?
Note: the definition of the operator doesn't even appears in the list of operator<< found.
I encountered the same problem and I think I figured out what was going on. For some reason, MSVC sometimes mistakes std::endl, for the endl defined in QTextStream (of course if you are "using namespace std" anywhere, this behavior is appropriate).
Also, I think MSVC sometimes gets confused with std::strings (maybe if they are const or addresses or something like that) with QTextStream.
MSVC tends to be very particular about the const/& variants of overloads, especially in cases where there may be some ambiguity. I have seen this before with non-QT code with some overloaded functions.
Of course, the error messages are just confusing so, it might be that my analysis is wrong here.
It is hard to say without seeing the actual instantiation site, but so far what I noticed is that the error says that there is no suitable operator for QTextStream, and your implementations use QTextStream&. This might be because you are trying to use the operator on an R-Value, these can be converted to const &, but not only &.
You forgot to make the overload that took a const Vector actually const.

Resources