-
|
|
|
Can you run unsafe C++ on the Xbox 360? I have found multiple sources which say I can and can't.
Also, what is defined as "safe" C++? Does that mean no * pointers?
How do I set up and configure a C++ project to deploy to the Xbox? Do I have to use the XNA command line tools?
I am currently working on a native Direct3D/OpenGL C++ framework that I would like to get running on my Xbox 360. I don't mind if it requires CLR instead of native (it's one extra compile switch) but I hope I don't have to go through and make every pointer a ^ GC pointer.
|
|
-
|
|
|
Unsafe, no. You're pretty much going to be out of luck here. Setting the compiler switch isn't going to help you if you interface with OpenGL/DirectX directly. In order to work on the Xbox, you cannot have any external native library references, including the Standard C Library. You have to call through to the .NET equivalents, if they exist. Also, you can't have any code that compiles down into native code.
Raw pointers may be okay (I'm not sure if they work under clr:safe), but you'd only be able to use them with pinned .NET objects. There is no "new/malloc" nor even Marshal.Alloc() on Xbox. The only memory allocator you have is "gcnew". So again, their usefulness is severly limited.
Basically, C++ through XNA Game Studio on Xbox is too limiting to do anything useful. It would probably be less hassle if you just ported to C#.
The easiest way to deploy is to package all of your C++/CLI code as an assembly, then create a C# stub (as an Xbox Game) which just calls into the assembly.
|
|
-
-
- (30328)
-
Member
MVP
Moderator
-
Posts
16,136
|
|
[some of this covered in the FAQ http://forums.xna.com/thread/7267.aspx]
We should be careful to use the right words for things here
'unsafe' is a term meaning that the code is still IL but no longer verifiable that comes with a managed language like C# or C++/CLI - and the 360 does support that kind of unsafe code.
'native' means there is precompiled x86 code in the EXE - and in this case you can't run that on the 360 obviously.
The issue for C++/CLI appears to be that there is no compiler switch to say 'make IL code, but don't make it verifiable'. See http://msdn2.microsoft.com/en-us/library/k8d11d4s.aspx and http://msdn2.microsoft.com/en-us/library/85344whh.aspx
/clr mixes native and IL
/clr:pure isn't sufficient (see Deans post http://blog.deanoc.com/?p=75 - though I am unsure why)
/clr:safe works but that won't allow pointers even though its supported on the 360 becuase it forces verifiable code. http://blog.deanoc.com/?p=74
But Shaw is correct about the hassles - safe/pure in C++ is quite different to writing native C++ and there is certainly no Pinvoke/COM/interop etc on the 360. The only real advantage would be if you really love C++ and can't bear the few subtle differences to switch to c#.
And yes there is no integration to the 360 or content pipeline so you need to use MS Build command line type tools to do that or other projects in your solution.
|
|
-
|
|
|
I found this: Scroll down to the 6th post. http://forums.scummvm.org/viewtopic.php?t=2060&postdays=0&postorder=asc&start=30So it's possible to run some /cr:pure code. Here are a few things I'm concerned about though: - I use new and delete a lot. However most of my pointers are warpped in smart pointers so I don't rely on delete in my code (other than in my garbage collector). So I could possibly use:
#define new gcnew and ignore delete in my garbage collector. But I don't want to do this unless I have to (I would still like unsafe pointers to work). - I rely on std::string and std::list. This isn't such an issue since I can write my own string class and my own linked-list template class.
- If unsafe classes aren't allowed I could use:
#define class ref class but that seems a bit dodgy to be elegent.
In response to my Direct3D/OpenGL code, all Direct3D code is in Direct3D.cpp and all OpenGL code is in OpenGL.cpp. Both of these inherit from my IRenderer interface. So I would place all XNA rendering code inside of XNA.cpp (and do the same for input, network, storage, etc). All Windows handling code (creating windows, displaying message boxes) is inside Windows.cpp which inherits from IPlatform, so I could place all Xbox/XNA code inside of XNA.cpp. I've written my framework to be modular and not tied down to one system like that. I don't mind using the command line tools for deploying to the Xbox (I learnt C programming with GCC, Make, and notepad). Visual Studio will help since you can add custom commands to run before running.
|
|
-
|
|
|
/clr:pure will work in a lot of cases, but it can produce code that the Xbox CLR cannot handle. So you have to be careful. It's not guaranteed to work. MessiahAndrw: I use new and delete a lot. However most of my pointers are warpped in smart pointers so I don't rely on delete in my code (other than in my garbage collector). So I could possibly use: #define new gcnew and
ignore delete in my garbage collector. But I don't want to do this
unless I have to (I would still like unsafe pointers to work).
No new and delete allowed (no standard C/C++ library period.) You can use gcnew (I think, haven't tried it) but then you're in managed memory and need to pin your pointers before doing anything pointer-esque with them.
MessiahAndrw: I rely on std::string and std::list. This isn't such an issue since I
can write my own string class and my own linked-list template class.
You cannot use the STL. You can write your own (though remember you still need to do so using managed memory, which can be a real pain!), but it would be advisable to use the .NET equivalents (System::String and System::Collections::Generic::List). Of course this would break compatibility with your existing code.
MessiahAndrw: If unsafe classes aren't allowed I could use: #define class ref class but that seems a bit dodgy to be elegent.
Don't forget that now you'll need to use GC pointers (^) for all of your class pointers. I know it's disheartening, but XNA Game Studio is just not set up to work with existing C++ code on Xbox.
|
|
-
|
|
|
ShawMishrak:No new and delete allowed (no standard C/C++ library period.) You can use gcnew (I think, haven't tried it) but then you're in managed memory and need to pin your pointers before doing anything pointer-esque with them.
But, I can write my own new/delete and disable the inbuilt STL's, yes? I am basing this on the assumption that /clr:pure will work and allows me to use unsafe pointers as in that post I provided a link to: (I'll be careful, and after a bit of trial and error I can find out what code can/cannot work on the Xbox.) I could port an a malloc implementation (e.g. liballoc), which will provide me with malloc/free. I can wrap new/delete around malloc/free (same thing, one's C++ and the other's C). liballoc requires 2 hook functions: allocate page and deallocate page. Typically these functions will ask the OS for a chunk of memory (at least 4KB) and liballoc will handle subdividing this memory up. So my allocate hook function can use gcnew to allocate a chunk of memory (an array of chars the size of the chunk of memory I want) and return a pointer to that. As long as I have a GC pointer (^) refering this this memory somewhere, this memory won't be released, right? And then for my deallocate function I simply null the pointer and XNA's garbage collector should take over from it. It's not that much work (just writing 2 functions); it's still easier than in the days of GBA programming. :) Then I can still use new/delete and don't need to use ref classes or ^. :) Nothing is impossible!
|
|
-
|
|
|
I admire your dedication, I really do. But I don't think you understand how managed memory works. I would highly recommend reading up on how memory management and garbage collection occurs in .NET so you can get a better understanding for why this is not so great of an idea. Then, read up on how C++/CLI and C# is translated into IL and compiled at run-time. Sure, you can write your own new/delete functions, but only for data (see below). It would be extremely ugly and involve holding onto a pin_ptr for your array representing your memory. Since pin_ptr cannot be held globally (only internally to a function), you would need to allocate enough memory for your entire memory footprint up front, pin it, and hold on to the pointer. It would look something like this: array<char>^ memoryArray = gcnew array<char>(100 * 1048576); // 100 MB pin_ptr<char> pinnedPtr(&memoryArray[0]); globalMemory = pinnedPtr; where globalMemory is a global char* pointer. You would need to keep track of what parts of that array are "allocated" and "free". Your new function would find a sufficiently sized chunk of space in the array, and delete would mark it as free. Basically, you would need to implement your own memory manager, not just new/delete. You cannot just call gcnew whenever you need memory because you need to keep the memory pinned if you want to retain native pointers to it. This also comes with the ugly side effect of not leaving any memory left for the XNA Framework or the .NET class library and any allocations it needs to do may fail. At the end of the day it's a lot more work than writing two functions. Also, you still cannot avoid ref classes. In order to make any sort of function call, you need to use managed code which won't be able to handle your native pointers. You cannot just create a standard C++ class, instantiate it, and make calls into it. Not even with your memory allocator. Even if you could construct a run-time instance of one of your classes in memory, you have no way of calling into it. Bottom Line:You need to use ref classes and GC handles (^) insteads of C++ classes and native pointers. There's no way around that for classes, not on Xbox. Doing you're own memory management with ugly hacks will only cause much pain and issues with the XNA Framework and .NET Class Library.
|
|
-
|
|
|
....I mentioned writing 2 functions meaning writing the two hooks in liballoc (to allocate and release memory using the GC). There are lots of other malloc implementations (I wrote my own first-fit for an extremely old OS kernel I was working on that used flat memory management, but I've since re-written my kernel to use paging and let user-programs handle their own MM (which I still needed to write my own malloc/free for my IPC system since it's a microkernel). Anyway that's getting off the topic.) So you can say I do know a little about memory management. I admit my knowledge on how .Net handles memory is not complete, but I am learning. This is what I've learnt so far: - gcnew allocates a chunk of memory and gives it a reference counter.
- ^ is a smart pointer.
- When the value of a ^ pointer is set to a chunk of memory the reference counter for that chunk of memory is increased.
- When the value of a ^ pointer is changed or a ^ pointer is destroyed, the reference counter for that chunk of memory the ^ pointer was pointing to is decreased.
- When the garbage collector goes does a clean up it looks for any chunk of memory whos reference counter <= 0 and frees that chunk of memory.
Knowing this system, I could manipulate it so that my memory manager can allocate one huge chunk of memory using gcnew. This chunk of memory will be added to a global table of allocated chunks of memory. When I use my memory manager to allocate/free memory, it allocates/frees memory which exists within one of these chunks of memory. In essence, requesting a chunk of memory from .Net is the same as a user program requesting a page from the operating system. If a chunk of memory is no longer required I just make sure that every ^ pointer pointing to that chunk of memory is null'ed. These ^ pointers will only need to be stored in my memory manager, since it is my memory manager that calling gcnew. There should be no reason the system would run out of memory (unless there was a bug, e.g. not all chunks of memory were being cleared). Granted, a little more memory will be used since I'll essentially have one form of memory management running on top of another. There won't be much more overheard in terms of performance, since I'll be using my own functions to allocate/free memory, and the only time gcnew or the garbage collector will be called will be when I need to allocate another chunk of memory for my memory manager to use. I do not see why you can't call of member function of a dereferenced * pointer? How is this "unsafe"? I am not pointing to a function, only to a class, and the function will always exist in a place in memory, the only difference is that I'm updating the *this pointer when I call that function. I'll play around and see why. If it can't be done then it can't be done, but I won't be content unless I'm given a reason why not. I can always try :) Some people (including me) said you couldn't write an operating system in C# - but someone did and it shut us all up (see SharpOS).
|
|
-
|
|
|
I'm not saying that it just can't be done. You can hack at the code all you want and probably come up with something that works in some cases. You left out one important detail in your description of managed memory and GC handles: you need to pin a GC handle before you can get a usable pointer from it, and then never release the pin over the lifespan of any pointers you have to it. If you don't pin, you can end up with bogus pointers after a garbage collection. Let me first state my concerns: - You say you won't run out of memory. How do you plan on balancing your local store of managed memory and the total memory on the system? Remember that the .NET Class Library and the XNA Framework need memory too, and that's going to come from the managed heap. If you allocate too much memory for yourself up front, you'll get OutOfMemory exceptions being thrown when you try to run XNA code. Remember that once you make a call into your game and start using pointers, you can no longer allocate memory for your memory manager. If you don't understand why, take a look at my post regarding pin_ptr above and read about it on Google. In short, managed memory is not stationary. A GC handle is not some fancy notation to refer to a memory location. The actual pointer behind the memory can be moved at run-time when the GC compacts the heap, so if you just blindly find a way to get a char* pointer out of a GC handle, it's not guaranteed to be valid afer a garbage collection! In order to get around this, you need to pin the memory and keep it pinned over the lifespan of your pointers. The only way to do this is within a function with pin_ptr, and the pinning is not valid after the function returns. That's why you need to allocate all you need up front, then call into your main message loop without exiting the function. You'll end up having to decide how much to allocate up front, and how much to leave to the .NET framework, and not change that amount over the life of the program.
- Calling into a method through a class pointer cannot be done because the Compact Framework just has no idea how to handle it. All it knows is calling into managed objects. When you just give it a memory address and tell it to make a call into it, it just chokes and fails to JIT the method. You cannot even do stack-allocated objects. The following will not work:
class MyClass { public: void MyMethod() { }; };
namespace Test { public ref class TestClass { public: static void DoSomething() { MyClass cls; cls.MyMethod(); }; }; }; The Compact Framework will just throw a MissingMethod exception when you call Test::TestClass::DoSomething() because it failed to compile that method. Of course code like this works in C++/CLI on the Desktop CLR, but the Compact Framework can not handle it. You cannot even use global functions without using /clr:safe (I'm not entirely sure why, but globals do not work in /clr or /clr:pure), but then bye-bye pointers! I'm not trying to be intentionally discouraging. I'm just trying to explain to you why others have not just recompiled their C++ code into IL and got it to run on the Xbox. I also understand that you have a good understanding of memory management, but that's not really the point here. You can be intimately familiar with paging algorithms defragmentation schemes, but the fact remains that you're going to be too limited with what you can do with C++ through XNA on Xbox. By all means go ahead and try, it would be a great learning exercise.
|
|
-
|
|
|
Okay that's simple enough :) Every pointer in my framework goes through a my SmartPointer object which contains the only * pointer in the entire framework so I could use (assuming MANAGED if defined when compiling for XNA/Xbox 360 and "T" is template<ref class>): #ifdef MANAGED T ^pointer; #else T *pointer; #endif Then I'll add make it disable my framework's garbage collector so it uses .Net's, and lastly I'll add the following to one of my global headers (such as Types.h which gets included into every file): #ifdef MANAGED #define class ref class #define new gcnew #endif Then with some more help of the preprocessor I'll make it compile my XNA IRenderer instead of OpenGL and D3D (and do the same with IPlatform, IInputManager, etc). If all else fails I'm looking into registering as a Middleware developer and using XeDK.
|
|
|