I know gcroot is used for holding a refrence to a managed object in a native class, but what about using managed objects as local variables inside an unmanaged class function ?
The compiler does not seem to generate an error on this, but is it "proper" ? Does it hurt performence?
There is no such thing as a local managed object. All managed objects are stored on the heap, required to let the garbage collector do its job. You could only have a reference as a local variable. A pointer at runtime.
Using an managed object reference in an unmanaged function is possible if you compile that code with /clr or #pragma managed in effect. Such code will be translated to IL and gets just-in-time compiled at runtime, just like normal managed code. It won't otherwise have the characteristics of managed code, there is no verification and you'll suffer all the normal pointer bugs. And yes, it can hurt performance because such code doesn't get the normal optimizer love. The optimizer built in the jitter isn't as effective because it works under time constraints.
Compile native code without the /clr option or use #pragma unmanaged inside your code to switch the compiler on-the-fly.
Managed objects, both values of managed value types (value struct, value class, enum class) and handles to managed reference types (ref struct, ref class) can be used inside code which is compiled to MSIL.
And code that is compiled to MSIL can be part of unmanaged objects (for example, a virtual member function of a standard C++ type can be compiled to MSIL, and the Visual C++ compiler "It Just Works" technology will make sure that the v-table is set up correctly). This is extremely useful when forwarding events and callbacks produced by standard C++ code into the managed GUI world. But it is also applicable if you have an algorithm implemented in managed code (perhaps C#) that you want to call from C++.
As Hans mentions, there are performance implications of switching between MSIL and machine code generation for a particular function. But if you are sitting on a native-managed boundary, then compilation to MSIL and use of the "It Just Works" a/k/a "C++ interop" is by far the highest performance alternative.
Related
I am going back to school where we have to take a C++ class.
I am familiar with the language but there's a few things I have never heard of...
Generally, my teacher said that plain C++ is "unsafe". It generates "unsafe code" (whatever that means). That's why we have to use C++/CLI which is supposed to make "safe" code.
Now... isn't CLI just a Microsoft .NET extension?
He is also telling us to use Console::WriteLine() instead of cout. Since Console::WriteLine() is "safe" and cout is "unsafe".
All this seems weird to me... Can anyone clarify this?
Thanks!
To put it very blunt and simple.
Safe
By "safe code" you teacher probably means managed code. That is code where you don't have to "care" about pointers and memory, you have a garbagecollector that takes care of this for your. You are dealing with refrences. Examples of languages built like this is java and c#. Code is compiled to a "fictional" opcodes(intermediate language, IL for C#), and compiles and run realtime(JIT, just in time compilation). The IL generated code, will have to be converted to real native platform based opcodes, in java this is one of things the jvm does. You may easily disassemble code from languages like these. And they may run on several platforms without a recompilation.
Unsafe
By "unsafe code" the teacher means ordinary native c++ unmanaged code, where all memory and resource management is handled by you. This makes room for human error, and memory leaks, resource leaks and other memory errors, you don't usually deal with in managed languages. It also compiles to pure bytecode (native assembly opcodes), which means that you have to compile your code for each platform you intend to target. You will encounter that you will have to make a lot of code specific for each platform, depending on what you are going to code. It's nice to see that simple things such as threading, which where platform dependent, now is a part of the c++ standard.
Then you have c++/CLI, which basicly is a mix. You may use managed code from the .net framework in c++, and it may be used as a bridge, and be used to make wrappers.
Console::WriteLine() is managed .net code, safe.
cout is standard iso c++ from <iostream>, unsafe
You find a related post here, with a broader answer here and here :)
Edit
As pointed out by Deduplicator below this is also of interest for you
Hope it helps.
Cheers
In the world of .NET, "safe" is synonymous with "verifiable" type safety. In Visual C++, it's enabled by /clr:safe.
/clr:safe will prevent you from using std::cout or any other function or type implemented in native code, because the metadata needed by .NET's verifier does not exist for native functions. MSIL which Stigandr mentioned can be used for just-in-time compilation, but even when compilation to native code is performed ahead of time, the MSIL is provided alongside the compiled native code and serves as a proof of its type safety which the verifier inspects.
Standard (native / unmanaged) C++ does check type safety during compilation. But that can be disabled by casts, and without runtime type checks, which C++ does not provide as part of the language, pointer arithmetic (e.g. array index out of bounds) can also violate type safety, as can using pointers to freed objects. C++ isn't just a language though, it is also a standard library, where you find smart pointers and smart collections that do the necessary runtime checks, so it can be just as type-safe as any managed framework.
I have added a class to my program and tested it. I was really surprised that there was any real errors. Here is the code:
#pragma once
#include "Iingredient.h"
#include <string>
#include <vector>
using namespace std;
ref class Recipe{
private:
string partsName;
vector<Iingredient> ing;
public:
Recipe(){}
};
And here are the errors:
Error 23 error C4368: cannot define 'partsName' as a member of managed
'Recipe': mixed types are not
supported c:\users\user\documents\visual studio
2010\projects\smestras2_l1\Recipe.h 10 1 file2_L1
Error 24 error C4368: cannot define 'ing' as a member of managed
'Recipe': mixed types are not
supported c:\users\user\documents\visual studio
2010\projects\smestras2_l1\Recipe.h 11 1 file2_L1
I googled a bit and found out that its about managed and unmanaged code.
How to fix this? Is it related with manged and unmanaged code or not? if so how?
I agree with others: you shouldn't use C++/CLI in most circumstances, you should use C# (or another "normal" managed language) for that (assuming you want to write a .Net application). C++/CLI is useful mostly in special circumstances, like interoperating between managed and unmanaged code.
If you're sure you want use C++/CLI, you can't put native classes into managed ones. But you can put pointers to native classes there:
ref class Recipe{
private:
string* partsName;
vector<Iingredient>* ing;
};
The code above works. But you have to keep in mind that those are normal native C++ pointers and that means you have to manually delete them. To do that property, you should read about how destructors and finalizers work in C++/CLI.
When defining ref class Recipe, you made it managed. But std::string and std::vector are umanaged types. You are trying to declare native variables in managed class, but it is not allowed.
Seems, you are novice in C++. Just, don't use C++/CLI. Consider C#, if you target .Net or unmanaged C++.
You cannot use unmanaged types in a managed class, ref keyword, because the heap and the managed heap are two different types of memory. To solve this specific problem you can use a managed string type (System::String^). The carrot tells the compiler that the type is a handle to a managed class.
Another way of going about this problem is to use pointers, that way the pointer will be on the managed heap, and the actual memory for that object will be in the standard unmanaged heap, which is where Recipe is located.
To construct your Recipe class you would have to do
Recipe^ recipe = gcnew Recipe();
With no idea of c++-cli, I can try and guess that the problem is that you are defining a reference type and trying to use C++ types inside (a std::string field) rather than whatever the equivalent managed type is (String?).
The reason why this can be problematic is that it mixes resource management approaches. Reference types are intended to be used out of a garbage collector, and unless you implement a destructor or IDisposable will just be ignored once proven that the last reference is lost. On the other hand, to be able to manage the memory in the internal field the std::string destructor must be called.
How would you build a wrapper to unmanaged code in order to use it in managed code, and when exactly do you have to do that?
You don't often need a wrapper, many DLLs with straight-forward exported C functions can be pinvoked with the [DllImport] attribute. An exception for C exports would be a poorly designed DLL that requires the client code to release memory, that can't be done by the managed code since it doesn't have access to the allocator.
The case where you have to have a wrapper is a native C++ class. Managed code cannot pinvoke it directly since it doesn't know how to create an instance of the class (which requires knowing the size of the object and calling the constructor) nor how to destroy it (which requires calling the destructor). It is pretty easy to do in C++/CLI. Very mechanical, the SWIG project can do it automatically. Learning that tool is however more of an investment than learning how to write the wrapper.
I need to work on converting a very huge c++ project to clr safe. The current c++ project has a lot of stuff from c++ like templates, generics, pointers, storage/stream, ole apis, zlib compression apis, inlines etc. Where can I find the datiled document for this type of conversion? Can you suggest some good book to refer to? If anyone of you have done such conversion, can I get some analysis from you?
I'll just cough up the MSDN Library article titled "How to: Migrate to /clr:safe
Visual C++ can generate verifiable components with using /clr:safe, which causes the compiler to generate errors for each non-verifiable code construct.
The following issues generate verifiability errors:
Native types. Even if it isn't used, the declaration of native classes, structures, pointers, or arrays will prevent compilation.
Global variables
Function calls into any unmanaged library, including common language runtime function calls
A verifiable function cannot contain a static_cast Operator for down-casting. The static_cast operator can be used for casting between primitive types, but for down-casting, safe_cast or a C-Style cast (which is implemented as a safe_cast) must be used.
A verifiable function cannot contain a reinterpret_cast operator (or any C-style cast equivalent).
A verifiable function cannot perform arithmetic on an interior_ptr. It may only assign to it and dereference it.
A verifiable function can only throw or catch pointers to reference types, so value types must be boxed before throwing.
A verifiable function can only call verifiable functions (such that calls to the common language runtime are not allowed, include AtEntry/AtExit, and so global constructors are disallowed).
A verifiable class cannot use Explicit.
If building an EXE, a main function cannot declare any parameters, so GetCommandLineArgs must be used to retrieve command-line arguments.
Making a non-virtual call to a virtual function.
Also, the following keywords cannot be used in verifiable code:
unmanaged and pack pragmas
naked and align __declspec modifiers
__asm
__based
__try and __except
I reckon that will keep you busy for a while. There is no magic wand to wave to turn native C++ into verifiable code. Are you sure this is worth the investment?
The vast majority of native C++ is entirely valid C++/CLI, including templates, inlines, etc, except the CLR STL is rather slow compared to the BCL. Also, native C++ doesn't have generics, only templates.
The reality of compiling as C++/CLI is to check the switch and push compile, and wait for it to throw errors.
Rewriting native C++ into safe C++/CLI will result in a code that is syntactically different, but semantically same as C#. If that is the case, why not rewrite directly in C#?
If you want to avoid what is essentially a complete rewrite, consider the following alternatives:
P/Invoke. Unfortunately, I'm unfamiliar whether this would isolate safe from unsafe code. Even if it can perform the isolation, you'll need to wrap your existing C++ code into procedural, C-like API, so it can be consumed by P/Invoke. On a plus side, unless your API is excessively chatty, you get to keep (most of) your native performance.
Wrapping your C++ into out-of-process COM server and using COM Interop to consume it from the manged code. This way, your managed code is completely protected from any corruption that might happen at C++ end and can remain "safe". The downside is a performance hit that you'll get for out-of-process marshaling and the implementation effort you'll need to expend to correctly implement the COM.
Is it valid to develop a DLL in C++ that returns boost shared pointers and uses them as parameters?
So, is it ok to export functions like this?
1.) boost::shared_ptr<Connection> startConnection();
2.) void sendToConnection(boost::shared_ptr<Connection> conn, byte* data, int len);
In special: Does the reference count work across DLL boundaries or would the requirement be that exe and dll use the same runtime?
The intention is to overcome the problems with object ownership. So the object gets deleted when both dll and exe don't reference it any more.
According to Scott Meyers in Effective C++ (3rd Edition), shared_ptrs are safe across dll boundaries. The shared_ptr object keeps a pointer to the destructor from the dll that created it.
In his book in Item 18 he states, "An especially nice feature of
tr1::shared_ptr is that it automatically uses its per-pointer deleter
to eliminate another potential client error, the "cross-DLL problem."
This problem crops up when an object is created using new in one
dynamically linked library (DLL) but is deleted in a different DLL. On
many platforms, such cross-DLL new/delete pairs lead to runtime
errors. tr1::shared_ptr avoid the problem, because its default deleter
uses delete from the same DLL where the tr1::shared_ptr is created."
Tim Lesher has an interesting gotcha to watch for, though, that he mentions here. You need to make sure that the DLL that created the shared_ptr isn't unloaded before the shared_ptr finally goes out of scope. I would say that in most cases this isn't something you have to watch for, but if you're creating dlls that will be loosely coupled then I would recommend against using a shared_ptr.
Another potential downside is making sure both sides are created with compatible versions of the boost library. Boost's shared_ptr has been stable for a long while. At least since 1.34 it's been tr1 compatible.
In my opinion, if it's not in the standard and it's not an object/mechanism provided by your library, then it shouldn't be part of the interface to the library. You can create your own object to do the reference counting, and perhaps use boost underneath, but it shouldn't be explicitly exposed in the interface.
DLLs do not normally own resources - the resources are owned by the processes that use the DLL. You are probably better off returning a plain pointer, which you then store in a shared pointer on the calling side. But without more info it's hard to be 100% certain about this.
Something to lookout for if you expose raw pointers from a dll interface. It forces you to use the shared dll CRT, memory allocated in one CRT cannot be deallocated in a different CRT. If you use the shared dll CRT in all your modules ( dll's & exe's ) then you are fine, they all share the same heap, if you dont you will be crossing CRT's and the world will meltdown.
Aside from that issue, I agree with the accepted answer. The creation factory probably shouldn't define ownership & lifecycle management for the client code.
No it is not.
The layout of boost::shared_ptr<T> might not be the same on both sides of the DLL boundary. (Layout is influenced by compiler version, packing pragmas, and other compiler options, as well as the actual version of the Boost source code.)
Only "standard layout" (a new concept in C++11, related to the old "POD = plain old data" concept) types can safely be passed between separately-built modules.