diff --git a/src/Generator/Utils/TestsUtils.cs b/src/Generator/Utils/TestsUtils.cs new file mode 100644 index 00000000..79af15c3 --- /dev/null +++ b/src/Generator/Utils/TestsUtils.cs @@ -0,0 +1,52 @@ +using System.IO; +using Cxxi.Generators; + +namespace Cxxi.Utils +{ + public abstract class LibraryTest : ILibrary + { + readonly string name; + readonly LanguageGeneratorKind kind; + + public LibraryTest(string name, LanguageGeneratorKind kind) + { + this.name = name; + this.kind = kind; + } + + public virtual void Setup(DriverOptions options) + { + options.LibraryName = name + ".Native"; + options.GeneratorKind = kind; + options.OutputDir = "../gen/" + name; + options.GenerateLibraryNamespace = false; + + var path = "../../../tests/" + name; + options.IncludeDirs.Add(path); + + var files = Directory.EnumerateFiles(path, "*.h"); + foreach(var file in files) + options.Headers.Add(Path.GetFileName(file)); + } + + public virtual void Preprocess(Library lib) + { + } + + public virtual void Postprocess(Library lib) + { + } + + public virtual void SetupPasses(Driver driver, PassBuilder passes) + { + } + + public virtual void GenerateStart(TextTemplate template) + { + } + + public virtual void GenerateAfterNamespaces(TextTemplate template) + { + } + } +} diff --git a/tests/Hello/Hello.Tests.cs b/tests/Hello/Hello.Tests.cs index 3f4f217c..7e9e7acb 100644 --- a/tests/Hello/Hello.Tests.cs +++ b/tests/Hello/Hello.Tests.cs @@ -1,13 +1,43 @@ -using System; +using NUnit.Framework; +[TestFixture] public class HelloTests { - public static void Main (String[] args) + [Test] + public void TestHello() { var hello = new Hello(); hello.PrintHello("Hello world"); - Console.WriteLine("True =" + hello.test1(3, 3.0f).ToString()); - Console.WriteLine("False =" + hello.test1(2, 3.0f).ToString()); + Assert.That(hello.add(1, 1), Is.EqualTo(2)); + Assert.That(hello.add(5, 5), Is.EqualTo(10)); + + Assert.IsTrue(hello.test1(3, 3.0f)); + Assert.IsFalse(hello.test1(2, 3.0f)); + + var foo = new Foo { A = 4, B = 7 }; + Assert.That(hello.AddFoo(foo), Is.EqualTo(11)); + Assert.That(hello.AddFooPtr(foo), Is.EqualTo(11)); + Assert.That(hello.AddFooRef(foo), Is.EqualTo(11)); + + var bar = new Bar { A = 4, B = 7 }; + Assert.That(hello.AddBar(bar), Is.EqualTo(11)); + + var retFoo = hello.RetFoo(7, 2.0f); + Assert.That(retFoo.A, Is.EqualTo(7)); + Assert.That(retFoo.B, Is.EqualTo(2.0)); + + var foo2 = new Foo2 {A = 4, B = 2, C = 3}; + Assert.That(hello.AddFoo(foo2), Is.EqualTo(6)); + Assert.That(hello.AddFoo2(foo2), Is.EqualTo(9)); + + var bar2 = new Bar2 { A = 4, B = 7, C = 3 }; + Assert.That(hello.AddBar2(bar2), Is.EqualTo(14)); + } + + static void Main(string[] args) + { + var hello = new Hello(); } } + \ No newline at end of file diff --git a/tests/Hello/Hello.cpp b/tests/Hello/Hello.cpp index 823f1e8d..c80316f5 100644 --- a/tests/Hello/Hello.cpp +++ b/tests/Hello/Hello.cpp @@ -1,18 +1,67 @@ -#include <iostream> #include "Hello.h" -using namespace std; +Foo::Foo() +{ +} + +Bar::Bar() +{ +} Hello::Hello () { + //cout << "Ctor!" << "\n"; } void Hello::PrintHello(const char* s) { - cout << "PrintHello: " << s << "\n"; + //cout << "PrintHello: " << s << "\n"; } bool Hello::test1(int i, float f) { return i == f; } + +int Hello::add(int a, int b) +{ + return a + b; +} + +int Hello::AddFoo(Foo foo) +{ + return (int)(foo.A + foo.B); +} + +int Hello::AddFooRef(Foo& foo) +{ + return AddFoo(foo); +} + +int Hello::AddFooPtr(Foo* foo) +{ + return AddFoo(*foo); +} + +int Hello::AddFoo2(Foo2 foo) +{ + return (int)(foo.A + foo.B + foo.C); +} + +int Hello::AddBar(Bar bar) +{ + return (int)(bar.A + bar.B); +} + +int Hello::AddBar2(Bar2 bar) +{ + return (int)(bar.A + bar.B + bar.C); +} + +Foo Hello::RetFoo(int a, float b) +{ + Foo foo; + foo.A = a; + foo.B = b; + return foo; +} \ No newline at end of file diff --git a/tests/Hello/Hello.cs b/tests/Hello/Hello.cs index 05157825..93d762ae 100644 --- a/tests/Hello/Hello.cs +++ b/tests/Hello/Hello.cs @@ -1,45 +1,19 @@ using Cxxi.Generators; -using Cxxi.Passes; +using Cxxi.Utils; namespace Cxxi.Tests { - class Hello : ILibrary + public class Hello : LibraryTest { - private readonly LanguageGeneratorKind kind; - public Hello(LanguageGeneratorKind kind) - { - this.kind = kind; - } - - public void Setup(DriverOptions options) - { - options.LibraryName = "Hello"; - options.GeneratorKind = kind; - options.OutputDir = kind == LanguageGeneratorKind.CPlusPlusCLI ? - "cli" : "cs"; - options.Headers.Add("Hello.h"); - options.IncludeDirs.Add("../../../examples/Hello"); - } - - public void Preprocess(Library lib) - { - } - - public void Postprocess(Library lib) - { - } - - public void SetupPasses(Driver driver, PassBuilder p) - { - } - - public void GenerateStart(TextTemplate template) + : base("Hello", kind) { } - public void GenerateAfterNamespaces(TextTemplate template) + public override void Preprocess(Library lib) { + lib.SetClassAsValueType("Bar"); + lib.SetClassAsValueType("Bar2"); } static class Program diff --git a/tests/Hello/Hello.h b/tests/Hello/Hello.h index 859c8d33..fc604330 100644 --- a/tests/Hello/Hello.h +++ b/tests/Hello/Hello.h @@ -1,14 +1,55 @@ +//#include <string> + #if defined(_MSC_VER) #define CXXI_API __declspec(dllexport) #else -#define CXXI_API +#define CXXI_API #endif +class CXXI_API Foo +{ +public: + + Foo(); + int A; + float B; +}; + +class CXXI_API Foo2 : public Foo +{ +public: + + int C; +}; + +struct CXXI_API Bar +{ + Bar(); + int A; + float B; +}; + +struct CXXI_API Bar2 : public Bar +{ + int C; +}; + class CXXI_API Hello { public: - Hello (); + Hello (); + + void PrintHello(const char* s); + bool test1(int i, float f); + int add(int a, int b); + + int AddFoo(Foo); + int AddFooRef(Foo&); + int AddFooPtr(Foo*); + Foo RetFoo(int a, float b); + + int AddFoo2(Foo2); - void PrintHello(const char* s); - bool test1(int i, float f); + int AddBar(Bar); + int AddBar2(Bar2); }; diff --git a/tests/Hello/Interop.h b/tests/Hello/Interop.h deleted file mode 100644 index 4572a509..00000000 --- a/tests/Hello/Interop.h +++ /dev/null @@ -1,203 +0,0 @@ -// ------------------------------------------------------------------------------------------- // -// clix.hpp (from http://blog.nuclex-games.com/mono-dotnet/cxx-cli-string-marshaling) -// -// Marshals strings between .NET and C++ using C++/CLI (Visual C++ 2005 and later only). -// Faster and cleaner than the System::Interop method because it uses garbage collected memory. -// Use at your own leisure. No warranties whatsoever provided. -// -// Original code by Markus Ewald -// Updated version including several improvements suggested by Neil Hunt -// -// Licensed under the IBM CPL (free of charge, closed source commercial use is okay) -// ------------------------------------------------------------------------------------------- // -#pragma once - -#include <string> -#include <vcclr.h> - -// CLI extensions namespace -namespace clix { - - /// <summary>Encoding types for strings</summary> - enum Encoding { - - /// <summary>ANSI encoding</summary> - /// <remarks> - /// This is the default encoding you've most likely been using all around in C++. ANSI - /// means 8 Bit encoding with character codes depending on the system's selected code page. - /// <remarks> - E_ANSI, - - /// <summary>UTF-8 encoding</summary> - /// <remarks> - /// This is the encoding commonly used for multilingual C++ strings. All ASCII characters - /// (0-127) will be represented as single bytes. Be aware that UTF-8 uses more than one - /// byte for extended characters, so std::string::length() might not reflect the actual - /// length of the string in characters if it contains any non-ASCII characters. - /// <remarks> - E_UTF8, - - /// <summary>UTF-16 encoding</summary> - /// <remarks> - /// This is the suggested to be used for marshaling and the native encoding of .NET - /// strings. It is similar to UTF-8 but uses a minimum of two bytes per character, making - /// the number of bytes required for a given string better predictable. Be aware, however, - /// that UTF-16 can still use more than two bytes for a character, so std::wstring::length() - /// might not reflect the actual length of the string. - /// </remarks> - E_UTF16, E_UNICODE = E_UTF16 - - }; - - // Ignore this if you're just scanning the headers for informations! - /* All this template stuff might seem like overkill, but it is well thought out and enables - you to use a readable and convenient call while still keeping the highest possible code - efficiency due to compile-time evaluation of the required conversion path. - */ - namespace detail { - - // Get C++ string type for specified encoding - template<Encoding encoding> struct StringTypeSelector; - template<> struct StringTypeSelector<E_ANSI> { typedef std::string Type; }; - template<> struct StringTypeSelector<E_UTF8> { typedef std::string Type; }; - template<> struct StringTypeSelector<E_UTF16> { typedef std::wstring Type; }; - - // Compile-time selection depending on whether a string is managed - template<typename StringType> struct IfManaged { - struct Select { - template<typename TrueType, typename FalseType> - struct Either { typedef FalseType Type; }; - }; - enum { Result = false }; - }; - template<> struct IfManaged<System::String ^> { - struct Select { - template<typename TrueType, typename FalseType> - struct Either { typedef TrueType Type; }; - }; - enum { Result = true }; - }; - - // Direction of the marshaling process - enum MarshalingDirection { - CxxFromNet, - NetFromCxx - }; - - // The actual marshaling code - template<MarshalingDirection direction> struct StringMarshaler; - - // Marshals to .NET from C++ strings - template<> struct StringMarshaler<NetFromCxx> { - - template<Encoding encoding, typename SourceType> - static System::String ^marshal(const SourceType &string) { - // Constructs a std::[w]string in case someone gave us a char * to choke on - return marshalCxxString<encoding, SourceType>(string); - } - - template<Encoding encoding, typename SourceType> - static System::String ^marshalCxxString( - const typename StringTypeSelector<encoding>::Type &cxxString - ) { - typedef typename StringTypeSelector<encoding>::Type SourceStringType; - size_t byteCount = cxxString.length() * sizeof(SourceStringType::value_type); - - if(byteCount == 0) return System::String::Empty; - - // Copy the C++ string contents into a managed array of bytes - array<unsigned char> ^bytes = gcnew array<unsigned char>(byteCount); - { pin_ptr<unsigned char> pinnedBytes = &bytes[0]; - memcpy(pinnedBytes, cxxString.c_str(), byteCount); - } - - // Now let one of .NET's encoding classes do the rest - return decode<encoding>(bytes); - } - - private: - // Converts a byte array based on the selected encoding - template<Encoding encoding> static System::String ^decode(array<unsigned char> ^bytes); - template<> static System::String ^decode<E_ANSI>(array<unsigned char> ^bytes) { - return System::Text::Encoding::Default->GetString(bytes); - } - template<> static System::String ^decode<E_UTF8>(array<unsigned char> ^bytes) { - return System::Text::Encoding::UTF8->GetString(bytes); - } - template<> static System::String ^decode<E_UTF16>(array<unsigned char> ^bytes) { - return System::Text::Encoding::Unicode->GetString(bytes); - } - }; - - // Marshals to C++ strings from .NET - template<> struct StringMarshaler<CxxFromNet> { - - template<Encoding encoding, typename SourceType> - static typename detail::StringTypeSelector<encoding>::Type marshal( - System::String ^string - ) { - typedef typename StringTypeSelector<encoding>::Type StringType; - - // First, we use .NET's encoding classes to convert the string into a byte array - array<unsigned char> ^bytes = encode<encoding>(string); - - if(bytes->Length == 0) return StringType(); - - // Then we construct our native string from that byte array - pin_ptr<unsigned char> pinnedBytes(&bytes[0]); - return StringType( - reinterpret_cast<StringType::value_type *>(static_cast<unsigned char *>(pinnedBytes)), - bytes->Length / sizeof(StringType::value_type) - ); - } - - template<> static std::wstring marshal<E_UTF16, System::String ^>( - System::String ^string - ) { - // We can directly accesss the characters in the managed string - pin_ptr<const wchar_t> pinnedChars(::PtrToStringChars(string)); - return std::wstring(pinnedChars, string->Length); - } - - private: - // Converts a string based on the selected encoding - template<Encoding encoding> static array<unsigned char> ^encode(System::String ^string); - template<> static array<unsigned char> ^encode<E_ANSI>(System::String ^string) { - return System::Text::Encoding::Default->GetBytes(string); - } - template<> static array<unsigned char> ^encode<E_UTF8>(System::String ^string) { - return System::Text::Encoding::UTF8->GetBytes(string); - } - template<> static array<unsigned char> ^encode<E_UTF16>(System::String ^string) { - return System::Text::Encoding::Unicode->GetBytes(string); - } - - }; - - } // namespace detail - - // ----------------------------------------------------------------------------------------- // - // clix::marshalString() - // ----------------------------------------------------------------------------------------- // - /// <summary>Marshals strings between .NET managed and C++ native</summary> - /// <remarks> - /// This all-in-one function marshals native C++ strings to .NET strings and vice versa. - /// You have to specify an encoding to use for the conversion, which always applies to the - /// native C++ string as .NET always uses UTF-16 for its own strings. - /// </remarks> - /// <param name="string">String to be marshalled to the other side</param> - /// <returns>The marshaled representation of the string</returns> - template<Encoding encoding, typename SourceType> - typename detail::IfManaged<SourceType>::Select::Either< - typename detail::StringTypeSelector<encoding>::Type, - System::String ^ - >::Type marshalString(SourceType string) { - - // Pass on the call to our nifty template routines - return detail::StringMarshaler< - detail::IfManaged<SourceType>::Result ? detail::CxxFromNet : detail::NetFromCxx - >::marshal<encoding, SourceType>(string); - - } - -} // namespace clix diff --git a/tests/Hello/premake4.lua b/tests/Hello/premake4.lua index 7ab72428..7918ed76 100644 --- a/tests/Hello/premake4.lua +++ b/tests/Hello/premake4.lua @@ -1,6 +1,2 @@ -group "Hello" - SetupTestGeneratorProject("Hello", "Hello.cs") - SetupTestNativeProject("Hello.Native", { "Hello.cpp", "Hello.h" }) - SetupTestProjects("Hello", { "Hello.Tests.cs"}) - - +group "Tests/Hello" + SetupTestProject("Hello") \ No newline at end of file