diff --git a/4120.pdf b/4120.pdf new file mode 100644 index 0000000..bbb62b6 Binary files /dev/null and b/4120.pdf differ diff --git a/4121.pdf b/4121.pdf new file mode 100644 index 0000000..82b4449 Binary files /dev/null and b/4121.pdf differ diff --git a/9781430210238.jpg b/9781430210238.jpg new file mode 100644 index 0000000..0d1d3e7 Binary files /dev/null and b/9781430210238.jpg differ diff --git a/Appendix/detecting_clr.cpp b/Appendix/detecting_clr.cpp new file mode 100644 index 0000000..cdfacbf --- /dev/null +++ b/Appendix/detecting_clr.cpp @@ -0,0 +1,10 @@ +// detecting_clr.cpp +#include +int main() +{ +#ifdef _MANAGED + System::Console::WriteLine("Must be compiling with /clr..."); +#else + printf("Not compiling with /clr."); +#endif +} diff --git a/Appendix/identifier.cpp b/Appendix/identifier.cpp new file mode 100644 index 0000000..efbb7d1 --- /dev/null +++ b/Appendix/identifier.cpp @@ -0,0 +1,21 @@ +// identifier.cpp +using namespace System; + +int main() +{ + int __identifier(switch) = 10; + + __identifier(switch)++; + + switch( __identifier(switch) ) + { + case 10: + break; + case 11: + Console::WriteLine("Switch is {0}", __identifier(switch)); + break; + default: + break; + } + +} diff --git a/Appendix/xml_comments.cpp b/Appendix/xml_comments.cpp new file mode 100644 index 0000000..c45b035 --- /dev/null +++ b/Appendix/xml_comments.cpp @@ -0,0 +1,27 @@ +// xml_comments.cpp +// compile with: /LD /clr /doc +// then run: xdcmake xml_comments.xdc + +using namespace System; + +/// Ref class R demonstrates XML Documentation Comments. +/// A class demonstrating documentation comments +/// A detailed description of R goes into the remarks block +/// +public ref class R +{ +public: + /// F is a method in the R class. + /// You can break the comments into paragraphs. + /// for related information. + /// + /// + void F(int i) {} + + /// The method G is a method in the R class. + /// Counts the number of characters in two strings. + /// Description for s1 + /// Description for s2 + /// The sum of the length of two strings. + int G(String^ s1, String^ s2){ return s1->Length + s2->Length; } +}; diff --git a/Chapter01/greeting.cpp b/Chapter01/greeting.cpp new file mode 100644 index 0000000..7d865a5 --- /dev/null +++ b/Chapter01/greeting.cpp @@ -0,0 +1,41 @@ +// greeting.cpp +using namespace System; + +value struct Greeting +{ + String^ greeting; + Char punctuator; + + void PrintGreeting(String^ name) + { + Console::WriteLine(greeting + name + punctuator); + } +}; + +int main(array^ args) +{ + Greeting greet; + greet.greeting = "Hi "; + greet.punctuator = '!'; + + if (args->Length < 1) + { + Console::WriteLine("Enter names on the command line, like this:" + " greeting ..."); + Console::WriteLine("Use quotes around names with spaces."); + return 1; + } + + for (int i = 0; i < args->Length; i++) + { + greet.PrintGreeting(args[i]); + } + + greet.greeting = "Hello, "; + greet.punctuator = '.'; + + for each (String^ s in args) + { + greet.PrintGreeting(s); + } +} diff --git a/Chapter01/hello_interop.cpp b/Chapter01/hello_interop.cpp new file mode 100644 index 0000000..85a773e --- /dev/null +++ b/Chapter01/hello_interop.cpp @@ -0,0 +1,53 @@ +// hello_interop.cpp + +#include +#include + +class HelloNative +{ + private: + // string is a Stanard C++ class (actually a typedef for basic_string) + // where each character is represented by a char + std::string* s; + + public: + HelloNative() + { + s = new std::string("Hello from native type!"); + } + void Greeting() + { + // The C++ basic_string class contains a method that returns a + // “C string” of type “const char *” for the C runtime function printf. + printf("%s\n", s->c_str()); + } + ~HelloNative() + { + delete s; + } +}; + +ref class HelloManaged +{ + private: + System::String^ s; + + public: + HelloManaged() + { + s = "Hello from managed type!"; + } + void Greeting() + { + System::Console::WriteLine("{0}", s); + } +}; + +int main() +{ + HelloNative* helloNative = new HelloNative(); + HelloManaged^ helloManaged = gcnew HelloManaged(); + + helloNative->Greeting(); + helloManaged->Greeting(); +} diff --git a/Chapter01/hello_world1.cpp b/Chapter01/hello_world1.cpp new file mode 100644 index 0000000..85e156e --- /dev/null +++ b/Chapter01/hello_world1.cpp @@ -0,0 +1,5 @@ +// hello_world1.cpp +int main() +{ + System::Console::WriteLine("Hello, World!"); +} diff --git a/Chapter01/hello_world2.cpp b/Chapter01/hello_world2.cpp new file mode 100644 index 0000000..d7ba3ee --- /dev/null +++ b/Chapter01/hello_world2.cpp @@ -0,0 +1,8 @@ +// hello_world2.cpp +#using "mscorlib.dll" +using namespace System; + +int main() +{ + Console::WriteLine("Hello World!"); +} diff --git a/Chapter01/hello_world3.cpp b/Chapter01/hello_world3.cpp new file mode 100644 index 0000000..e9829ee --- /dev/null +++ b/Chapter01/hello_world3.cpp @@ -0,0 +1,33 @@ +// hello_world3.cpp + +using namespace System; + +ref class Hello +{ + String^ greeting; + + public: + + Hello(String^ str) : greeting(str) + { } + + void Greet() + { + Console::WriteLine(greeting + "!"); + } + + void SetGreeting(String^ newGreeting) + { + greeting = newGreeting; + } +}; + +int main() +{ + Hello^ hello = gcnew Hello("Hi there!"); + hello->SetGreeting("Hello World"); + hello->Greet(); + hello->SetGreeting("Howdy"); + hello->Greet(); +} + diff --git a/Chapter01/hello_world4.cpp b/Chapter01/hello_world4.cpp new file mode 100644 index 0000000..89213fc --- /dev/null +++ b/Chapter01/hello_world4.cpp @@ -0,0 +1,25 @@ +// hello_world4.cpp +using namespace System; + +value class Hello +{ + String^ greeting = "Hi there"; + + public: + void Greet() + { + Console::WriteLine(greeting + "!"); + } + + void SetGreeting(String^ newGreeting) + { + greeting = newGreeting; + } + +}; + +int main() +{ + Hello hello; + hello.Greet("Hello World"); +} diff --git a/Chapter01/value_type_with_members.cpp b/Chapter01/value_type_with_members.cpp new file mode 100644 index 0000000..84972f2 --- /dev/null +++ b/Chapter01/value_type_with_members.cpp @@ -0,0 +1,14 @@ +// value_type_with_members.cpp + +using namespace System; + +value struct Greeting +{ + String^ greeting; + Char punctuator; + + void PrintGreeting(String^ name) + { + Console::WriteLine(greeting + name + punctuator); + } +}; diff --git a/Chapter02/atom.cpp b/Chapter02/atom.cpp new file mode 100644 index 0000000..1779713 --- /dev/null +++ b/Chapter02/atom.cpp @@ -0,0 +1,29 @@ +// atom.cpp +class Atom +{ + private: + double pos[3]; + unsigned int atomicNumber; + unsigned int isotopeNumber; + + public: + Atom() : atomicNumber(1), isotopeNumber(1) + { + // Let's say we most often use hydrogen atoms, + // so there is a default constructor that assumes you are + // creating a hydrogen atom. + pos[0] = 0; pos[1] = 0; pos[2] = 0; + } + + Atom(double x, double y, double z, unsigned int a, unsigned int n) + : atomicNumber(a), isotopeNumber(n) + { + pos[0] = x; pos[1] = y; pos[2] = z; + } + unsigned int GetAtomicNumber() { return atomicNumber; } + void SetAtomicNumber(unsigned int a) { atomicNumber = a; } + unsigned int GetIsotopeNumber() { return isotopeNumber; } + void SetIsotopeNumber(unsigned int n) { isotopeNumber = n; } + double GetPosition(int index) { return pos[index]; } + void SetPosition(int index, double value) { pos[index] = value; } +}; diff --git a/Chapter02/atom_managed.cpp b/Chapter02/atom_managed.cpp new file mode 100644 index 0000000..5fc3a4f --- /dev/null +++ b/Chapter02/atom_managed.cpp @@ -0,0 +1,28 @@ +// atom_managed.cpp +ref class Atom +{ + private: + array^ pos; // declare the managed array + unsigned int atomicNumber; + unsigned int isotopeNumber; + + public: + Atom() + { + // We'll need to allocate space for the position values. + pos = gcnew array(3); + pos[0] = 0; pos[1] = 0; pos[2] = 0; + atomicNumber = 1; + isotopeNumber = 1; + } + Atom(double x, double y, double z, unsigned int atNo, unsigned int n) + : atomicNumber(atNo), isotopeNumber(n) + { + // create the managed array + pos = gcnew array(3); + pos[0] = x; pos[1] = y; pos[2] = z; + } + // the rest of the class declaration is unchanged +}; + + diff --git a/Chapter02/atom_valuetype.cpp b/Chapter02/atom_valuetype.cpp new file mode 100644 index 0000000..952dce5 --- /dev/null +++ b/Chapter02/atom_valuetype.cpp @@ -0,0 +1,31 @@ +// atom_valuetype.cpp +value class Atom +{ + private: + array^ pos; // declare the managed array + unsigned int atomicNumber; + unsigned int isotopeNumber; + + public: + + void Initialize() + { + + } + // the rest of the class declaration is unchanged +}; + +void atoms() +{ + int n_atoms = 50; + array^ atoms = gcnew array(n_atoms); + + // Between the array creation and initialization, + // the atoms are in an invalid state. + // Don't call GetAtomicNumber here! + + for (int i = 0; i < n_atoms; i++) + { + atoms[i].Initialize( /* ... */ ); + } +} diff --git a/Chapter02/atom_with_enum.cpp b/Chapter02/atom_with_enum.cpp new file mode 100644 index 0000000..dc7ef47 --- /dev/null +++ b/Chapter02/atom_with_enum.cpp @@ -0,0 +1,52 @@ +// atom_with_enum.cpp + +using namespace System; + +value struct Point3D +{ + double x; + double y; + double z; + +}; + +enum class Element +{ + Hydrogen = 1, Helium, Lithium, Beryllium, Boron, Carbon, Nitrogen, Oxygen, + Fluorine, Neon + // ... 100 or so other elements omitted for brevity +}; + + +ref class Atom +{ + private: + Point3D position; + unsigned int atomicNumber; + unsigned int isotopeNumber; + + public: + Atom(Point3D pos, unsigned int a, unsigned int n) + : position(pos), atomicNumber(a), isotopeNumber(n) + { } + + // ... + + Element GetElementType() + { + return safe_cast( atomicNumber ); + } + void SetElementType(Element element) + { + atomicNumber = safe_cast(element); + } + String^ GetElementString() + { + return GetElementType().ToString(); + } + + + + // the rest of the code is unchanged + +}; diff --git a/Chapter02/atom_with_properties.cpp b/Chapter02/atom_with_properties.cpp new file mode 100644 index 0000000..3680320 --- /dev/null +++ b/Chapter02/atom_with_properties.cpp @@ -0,0 +1,62 @@ + +enum class Element +{ + Hydrogen = 1, Helium, Lithium, Beryllium, Boron, Carbon, Nitrogen, Oxygen, + Fluorine, Neon + // ... 100 or so other elements omitted for brevity +}; + +ref class Atom +{ + private: + array^ pos; + + public: + + Atom(double x, double y, double z, unsigned int a, unsigned int n) + { + pos = gcnew array(3); + pos[0] = x; pos[1] = y; pos[2] = z; + + AtomicNumber = a; + IsotopeNumber = n; + } + + property unsigned int AtomicNumber; + property unsigned int IsotopeNumber; + + property Element ElementType + { + Element get() + { + return safe_cast(AtomicNumber); + } + void set(Element element) + { + AtomicNumber = safe_cast(element); + } + } + + property double Position[int] + { + // if index is out of range, the array access will + // throw an IndexOutOfRange exception + double get(int index) { + return pos[index]; + } + void set(int index, double value) + { + pos[index] = value; + } + } + + virtual void AlphaDecay() + { + AtomicNumber -= 2; + IsotopeNumber -= 4; + UpdateHalfLife(); + } + + void UpdateHalfLife() { /* TODO */ } + +}; diff --git a/Chapter02/atom_with_valuetype.cpp b/Chapter02/atom_with_valuetype.cpp new file mode 100644 index 0000000..bdb7786 --- /dev/null +++ b/Chapter02/atom_with_valuetype.cpp @@ -0,0 +1,51 @@ +// value_struct.cpp +value struct Point3D +{ + double x; + double y; + double z; + +}; + +enum class Element +{ + Hydrogen = 1, Helium, Lithium, Beryllium, Boron, Carbon, Nitrogen, Oxygen, + Fluorine, Neon + // ... 100 or so other elements omitted for brevity +}; + + +ref class Atom +{ + private: + Point3D position; + unsigned int atomicNumber; + unsigned int isotopeNumber; + + public: + Atom(Point3D pos, unsigned int a, unsigned int n) + : position(pos), atomicNumber(a), isotopeNumber(n) + { } + + ref class Atom +{ + // ... + + Element GetElementType() + { + return safe_cast( atomicNumber ); + } + void SetElementType(Element element) + { + atomicNumber = safe_cast(element); + } + String^ GetElementString() + { + return GetElementType().ToString(); + } +}; + + + // the rest of the code is unchanged + +}; diff --git a/Chapter02/defining_interfaces.cpp b/Chapter02/defining_interfaces.cpp new file mode 100644 index 0000000..ff8c1f4 --- /dev/null +++ b/Chapter02/defining_interfaces.cpp @@ -0,0 +1,44 @@ +// atom_interfaces.cpp + +#include "atom_with_enum.cpp" + +interface class IRadioactive +{ + void AlphaDecay(); + void BetaDecay(); + double GetHalfLife(); +}; + +ref class RadioactiveAtom : Atom, IRadioactive +{ + double half_life; + + void UpdateHalfLife() + { + // ... + } + + + public: + // the atom releases an alpha particle + // so it loses two protons and two neutrons + virtual void AlphaDecay() + { + SetAtomicNumber(GetAtomicNumber() - 2); + SetIsotopeNumber(GetIsotopeNumber() - 4); + UpdateHalfLife(); + } + + // the atom releases an electron + // a neutron changes into a proton + virtual void BetaDecay() + { + SetAtomicNumber(GetAtomicNumber() + 1); + UpdateHalfLife(); + } + + virtual double GetHalfLife() + { + return half_life; + } +}; diff --git a/Chapter02/elements_enum.cpp b/Chapter02/elements_enum.cpp new file mode 100644 index 0000000..822498f --- /dev/null +++ b/Chapter02/elements_enum.cpp @@ -0,0 +1,8 @@ +// elements_enum.cpp + +enum class Element +{ + Hydrogen = 1, Helium, Lithium, Beryllium, Boron, Carbon, Nitrogen, Oxygen, + Fluorine, Neon + // ... 100 or so other elements omitted for brevity +}; diff --git a/Chapter02/elements_stlclr.cpp b/Chapter02/elements_stlclr.cpp new file mode 100644 index 0000000..9b112c0 --- /dev/null +++ b/Chapter02/elements_stlclr.cpp @@ -0,0 +1,22 @@ +// elements_stlclr.cpp + +#include + +using namespace cliext; // for STL/CLR +using namespace std; // for +using namespace System; + +typedef map PeriodicTable; + +int main() +{ + PeriodicTable elementTable; + elementTable.insert(PeriodicTable::make_value("Hydrogen", 1)); + elementTable.insert(PeriodicTable::make_value("Helium", 2)); + + for each( PeriodicTable::value_type element in elementTable) + { + Console::WriteLine("{0} {1}", element->first, element->second); + } + +} diff --git a/Chapter02/generic_list.cpp b/Chapter02/generic_list.cpp new file mode 100644 index 0000000..822089c --- /dev/null +++ b/Chapter02/generic_list.cpp @@ -0,0 +1,10 @@ +// generic_list.cpp + +generic +ref class List +{ + public: + T Add(T t) { /* ... */ } + void Remove(T t) { /* ...*/ } + // other methods +}; diff --git a/Chapter02/longlong.cpp b/Chapter02/longlong.cpp new file mode 100644 index 0000000..9c62090 --- /dev/null +++ b/Chapter02/longlong.cpp @@ -0,0 +1,6 @@ + +int main() +{ + //long long __int64 i; + long long j; +} \ No newline at end of file diff --git a/Chapter02/radioactive_decay.cpp b/Chapter02/radioactive_decay.cpp new file mode 100644 index 0000000..c367ccd --- /dev/null +++ b/Chapter02/radioactive_decay.cpp @@ -0,0 +1,131 @@ +// radioactive_decay.cpp +using namespace System; + +// This declares a delegate type that takes no parameters +delegate void DecayProcessFunc(); + +enum class Element +{ + Hydrogen = 1, Helium, Lithium, Beryllium, Boron, Carbon, Nitrogen, Oxygen, + Fluorine, Neon + // ... 100 or so other elements omitted for brevity +}; + +ref class Atom +{ + + + + public: + + Atom( unsigned int a, unsigned int n) + { + + AtomicNumber = a; + IsotopeNumber = n; + } + + property unsigned int AtomicNumber; + property unsigned int IsotopeNumber; + + property Element ElementType + { + Element get() + { + return safe_cast(AtomicNumber); + } + void set(Element element) + { + AtomicNumber = safe_cast(element); + } + } + + + + +}; + +ref class RadioactiveAtom : Atom +{ + + public: + RadioactiveAtom(int a, int n, bool is_stable, double half_life) + : Atom(a, n) + { + IsStable = is_stable; + HalfLife = half_life; + Lambda = Math::Log(2) / half_life; + } + + // the atom releases an alpha particle + // so it loses two protons and two neutrons + virtual void AlphaDecay() + { + AtomicNumber -= 2; + IsotopeNumber -= 4; + Update(); + } + + // the atom releases an electron + void BetaDecay() + { + AtomicNumber++; + Update(); + } + + property bool IsStable; + property double HalfLife; + property double Lambda; + void Update() + { + // In this case we assume it decays to a stable nucleus. + // nullptr is the C++/CLI way to refer to an unassigned handle. + DecayProcess = nullptr; + IsStable = true; + } + + // Declare the delegate property. We'll call this when + // an atom decays. + property DecayProcessFunc^ DecayProcess; + +}; // ref class RadioactiveAtom + +void SimulateDecay(int a, int n, double halflife, int step, + int max_time, int num_atoms, int seed) +{ + array^ atoms = gcnew array(num_atoms); + + // Initialize the array. + // We cannot use a for each statement here because the for each + // statement is not allowed to modify the atoms array. + for (int i = 0; i < num_atoms; i++) + { + atoms[i] = gcnew RadioactiveAtom(a, n, false, halflife); + // create the delegate + atoms[i]->DecayProcess = + gcnew DecayProcessFunc(atoms[i], &RadioactiveAtom::BetaDecay); + } + + Random^ rand = gcnew Random(seed); + for (int t = 0; t < max_time; t++) + { + for each (RadioactiveAtom^ atom in atoms) + { + if ((!atom->IsStable) && atom->Lambda * step > rand->NextDouble()) + { + // invoke the delegate + atom->DecayProcess->Invoke(); + } + } + } +} + +int main() +{ + // Carbon-14. Atomic Number: 6 Isotope Number 14 + // Half-Life 5730 years + // Number of atoms 10000 + // Maximum time 10000 + // Random number seed 7757 + SimulateDecay(6, 14, 5730, 1, 10000, 10000, 7757); +} diff --git a/Chapter02/value_struct.cpp b/Chapter02/value_struct.cpp new file mode 100644 index 0000000..9cfbc23 --- /dev/null +++ b/Chapter02/value_struct.cpp @@ -0,0 +1,8 @@ +// value_struct.cpp +value struct Point3D +{ + double x; + double y; + double z; + +}; diff --git a/Chapter03/file1.cpp b/Chapter03/file1.cpp new file mode 100644 index 0000000..c3e5a5c --- /dev/null +++ b/Chapter03/file1.cpp @@ -0,0 +1,5 @@ +// file1.cpp + +public ref class R +{ +}; diff --git a/Chapter03/file2.cpp b/Chapter03/file2.cpp new file mode 100644 index 0000000..65780f1 --- /dev/null +++ b/Chapter03/file2.cpp @@ -0,0 +1,11 @@ +// file2.cpp +#using "file1.dll" + +// we'll define a function, so we can see it in the metadata later +void F() +{ + R r; +} + +int main() +{} diff --git a/Chapter03/message_box.cpp b/Chapter03/message_box.cpp new file mode 100644 index 0000000..28b9edb --- /dev/null +++ b/Chapter03/message_box.cpp @@ -0,0 +1,7 @@ +// message_box.cpp +#include + +int main() +{ + ::MessageBox(0,"message","caption",MB_OK); +} diff --git a/Chapter03/mykey.snk b/Chapter03/mykey.snk new file mode 100644 index 0000000..ad5a97d Binary files /dev/null and b/Chapter03/mykey.snk differ diff --git a/Chapter03/reftype.cpp b/Chapter03/reftype.cpp new file mode 100644 index 0000000..8f83df1 --- /dev/null +++ b/Chapter03/reftype.cpp @@ -0,0 +1,26 @@ +// reftype.cpp +using namespace System; + +public ref class RefType +{ + String^ classname; + + public: + + RefType() + { + classname = gcnew String("RefType"); + } + + String^ GetMessage() + { + return String::Format("I am a " + classname); + } + +}; + +int main() +{ + RefType^ r = gcnew RefType(); + Console::WriteLine(r->GetMessage()); +} diff --git a/Chapter03/reftype.netmodule b/Chapter03/reftype.netmodule new file mode 100644 index 0000000..511fa67 Binary files /dev/null and b/Chapter03/reftype.netmodule differ diff --git a/Chapter03/using_directive.cpp b/Chapter03/using_directive.cpp new file mode 100644 index 0000000..400bc4a --- /dev/null +++ b/Chapter03/using_directive.cpp @@ -0,0 +1,9 @@ +// using_directive.cpp +#using "System.Windows.Forms.dll" + +using namespace System::Windows::Forms; + +int main() +{ + MessageBox::Show("Hello World!"); +} diff --git a/Chapter03/using_directive2.cpp b/Chapter03/using_directive2.cpp new file mode 100644 index 0000000..26cba97 --- /dev/null +++ b/Chapter03/using_directive2.cpp @@ -0,0 +1,7 @@ +// using_directive2.cpp +#using "System.Windows.Forms.dll" + +int main() +{ + System::Windows::Forms::MessageBox::Show("Hello World!"); +} diff --git a/Chapter04/ManagedPlantQuery.cpp b/Chapter04/ManagedPlantQuery.cpp new file mode 100644 index 0000000..eb2a1f4 --- /dev/null +++ b/Chapter04/ManagedPlantQuery.cpp @@ -0,0 +1,45 @@ +// ManagedPlantQuery.cpp +using namespace System; + +ref class Recordset; + +ref class DBConnection +{ + public: + DBConnection() + { + // Open the connection + // ... + } + Recordset^ Query(String^ search) + { + // query the database, generate recordset + // and return handle to recordset + // ... + } + ~DBConnection() + { + // Close the connection + // ... + } +}; + +ref class PlantData +{ + public: +/* static Recordset^ PlantQuery(String^ search) + { + DBConnection^ connection = gcnew DBConnection(); + return connection->Query( search ); + } +*/ + + static Recordset^ PlantQuery(String^ search) + { + DBConnection^ connection = gcnew DBConnection(); + Recordset^ records = connection->Query( search ); + delete connection; + return records; + } + +}; diff --git a/Chapter04/ManagedPlantQuery2.cpp b/Chapter04/ManagedPlantQuery2.cpp new file mode 100644 index 0000000..7915a06 --- /dev/null +++ b/Chapter04/ManagedPlantQuery2.cpp @@ -0,0 +1,52 @@ +// ManagedPlantQuery2.cpp +using namespace System; + +ref class Recordset; + +ref class DBConnection +{ + public: + DBConnection() + { + // Open the connection + // ... + } +/* Recordset^ Query(String^ search) + { + // query the database, generate recordset + // and return pointer to recordset + // ... + } +*/ + void Query(String^ search, Recordset^% records) + { + // query the database, generate recordset + // and set the records handle to point to it + records = gcnew Recordset(); + } + + ~DBConnection() + { + // Close the connection + // ... + } +}; + +ref class PlantData +{ + public: +/* static Recordset^ PlantQuery(String^ search) + { + DBConnection connection; + return connection.Query( search); + } +*/ + static Recordset^ PlantQuery(String^ search) + { + DBConnection connection; + Recordset^ records; + connection.Query( search, records ); + return records; + } + +}; diff --git a/Chapter04/PlantQuery.cpp b/Chapter04/PlantQuery.cpp new file mode 100644 index 0000000..d87a90d --- /dev/null +++ b/Chapter04/PlantQuery.cpp @@ -0,0 +1,33 @@ +// PlantQuery.cpp + +class Recordset; + +class DBConnection +{ + public: + DBConnection() + { + // Open the connection + // ... + } + void Query(char* search, Recordset** records) + { + // query the database, generate recordset + // ... + } + ~DBConnection() + { + // Close the connection + // ... + } +}; + +class PlantData +{ + public: + static void PlantQuery(char* search, Recordset** records) + { + DBConnection connection; + connection.Query( search, records); + } // destructor for connection called +}; diff --git a/Chapter04/auto_handle.cpp b/Chapter04/auto_handle.cpp new file mode 100644 index 0000000..12aff00 --- /dev/null +++ b/Chapter04/auto_handle.cpp @@ -0,0 +1,67 @@ +// auto_handle.cpp +#include + +using namespace System; +using namespace msclr; + +ref class DBConnection +{ + public: + bool Open() + { + // open a database connection (actual code omitted) + // ... + return true; + } + void Close() + { + // close the database connection + // ... + } +}; + +ref class PlantData +{ + DBConnection^ connection; + int id; + + PlantData(int i) : id(i) + { + if (connection->Open() == true) + { + Console::WriteLine("Opened connection for id {0}.", id); + } + } + + public: + + static PlantData^ GetPlantData(int id) + { + return gcnew PlantData(id); + } + + void Use() + { + Console::WriteLine("Using id {0}.", id); + // Query database + // Update records, etc. + } + + ~PlantData() + { + connection->Close(); + Console::WriteLine("Closing connection for id {0}.", id); + } +}; + +// Using stack semantics: destructor called. +void f_stack(int i) +{ + auto_handle data = PlantData::GetPlantData(i); + data->Use(); +} + +int main() +{ + f_stack(1); +} diff --git a/Chapter04/debug_print.cpp b/Chapter04/debug_print.cpp new file mode 100644 index 0000000..0f5f3ec --- /dev/null +++ b/Chapter04/debug_print.cpp @@ -0,0 +1,23 @@ +// debug_print.cpp +using namespace System; + +void DebugPrint(Object^ obj) +{ + // For debugging purposes, display the type of the object + // and its string conversion. + + System::Type^ type = obj->GetType(); + Console::WriteLine("Type: {0} Value: {1}", type->ToString(), + obj->ToString() ); + +} + +int main() +{ + + int i = 56; + DebugPrint(i); + + String^ s = "Time flies like an arrow; fruit flies like a banana."; + DebugPrint(s); +} \ No newline at end of file diff --git a/Chapter04/dereferencing_handles.cpp b/Chapter04/dereferencing_handles.cpp new file mode 100644 index 0000000..fa6156f --- /dev/null +++ b/Chapter04/dereferencing_handles.cpp @@ -0,0 +1,15 @@ + +ref class R +{ + public: + + R() {} + R(R% r) {} +}; + +int main() +{ + R^ r_handle = gcnew R(); + R r_auto = *r_handle; // copy ctor used + R% r_ref = *r_handle; // not copied +} \ No newline at end of file diff --git a/Chapter04/double_pointer.cpp b/Chapter04/double_pointer.cpp new file mode 100644 index 0000000..a26b8e4 --- /dev/null +++ b/Chapter04/double_pointer.cpp @@ -0,0 +1,9 @@ +// double_pointer.cpp +#include + +int newstring(void** new_buffer) +{ + *new_buffer = malloc( 1024 ); + if (! *new_buffer) return -1; + return 1; +} diff --git a/Chapter04/game_library2.cpp b/Chapter04/game_library2.cpp new file mode 100644 index 0000000..755cd37 --- /dev/null +++ b/Chapter04/game_library2.cpp @@ -0,0 +1,21 @@ +// game_library.cpp +ref class GameObject +{ + public: + void Initialize(); + virtual void Read() { /* read data from file */ } + virtual void Write() { /* write data to file */ } +}; + + +public ref class Monster : GameObject +{ +}; + +public ref class MapTile : GameObject +{ +}; + +public ref class Item : GameObject +{ +}; diff --git a/Chapter04/gc_lvalues.cpp b/Chapter04/gc_lvalues.cpp new file mode 100644 index 0000000..94fc585 --- /dev/null +++ b/Chapter04/gc_lvalues.cpp @@ -0,0 +1,42 @@ +// gc_lvalues.cpp + +value struct V +{ + int i; +}; + +ref class R +{ + public: + + V m_v; + +}; + +R^ GetRHandle() { return gcnew R(); } + +int main() +{ + // i is an lvalue, 12 is an rvalue + int i = 12; + + // an lvalue i2; i is used here as an rvalue + // lvalues can always be used as rvalues + int i2 = i; + V v; // value type on the stack + R r; // reference type (on the managed heap but with stack semantics) + + int& i3 = i; // native reference: an lvalue + int% i4 = i; // tracking reference: lvalues can be assigned gc-rvalues + int& i5 = v.i; // OK: v.i is a stack-based object + // int& i6 = r.m_v.i; // Illegal: r is a managed heap-based object + i4 = v.i; // OK: i4 is a gc-lvalue + + R^ r1, ^r2; // r1 and r2 are gc-lvalues + // gcnew R() is a gc-rvalue + r1 = gcnew R(); + // GetRHandle() is a gc-rvalue, too + r2 = GetRHandle(); + + R% r3 = *r1; // a gc-lvalue r3 is assigned to the gc-rvalue *r1 +} diff --git a/Chapter04/gcvalues.cpp b/Chapter04/gcvalues.cpp new file mode 100644 index 0000000..c4dc220 --- /dev/null +++ b/Chapter04/gcvalues.cpp @@ -0,0 +1,42 @@ +// gc_lvalues.cpp + +value struct V +{ + int i; +}; + +ref class R +{ + public: + + V m_v; + +}; + +R^ GetRHandle() { return gcnew R(); } + +int main() +{ + // i is an lvalue, 12 is an rvalue + int i = 12; + + // an lvalue i2; i is used here as an rvalue + // lvalues can always be used as rvalues + int i2 = i; + V v; // value type on the stack + R r; // reference type (on the managed heap but with stack semantics) + + int& i3 = i; // native reference: an lvalue + int% i4 = i; // tracking reference: lvalues can be assigned gc-rvalues + int& i5 = v.i; // OK: v.i is a stack-based object + int& i6 = r.m_v.i; // Illegal: r is a managed heap-based object + i4 = v.i; // OK: i4 is a gc-lvalue + + R^ r1, ^r2; // r1 and r2 are gc-lvalues + // gcnew R() is a gc-rvalue + r1 = gcnew R(); + // GetRHandle() is a gc-rvalue, too + r2 = GetRHandle(); + + R% r3 = *r1; // a gc-lvalue r3 is assigned to the gc-rvalue *r1 +} diff --git a/Chapter04/handle_to_valuetype.cpp b/Chapter04/handle_to_valuetype.cpp new file mode 100644 index 0000000..9789327 --- /dev/null +++ b/Chapter04/handle_to_valuetype.cpp @@ -0,0 +1,25 @@ +// handle_to_valuetype.cpp + +using namespace System; + +value struct V +{ + int a; + int b; +}; + +// function taking a handle to a value type +void f(V^ v) +{ + v->a = 10; + v->b = 20; +} + +int main() +{ + V v; + v.a = 1; + v.b = 2; + f(%v); // creates a copy of v + Console::WriteLine("{0} {1}", v.a, v.b); +} diff --git a/Chapter04/iref.cpp b/Chapter04/iref.cpp new file mode 100644 index 0000000..d0679f8 --- /dev/null +++ b/Chapter04/iref.cpp @@ -0,0 +1,6 @@ + +int main() +{ + int i = 110; + int% iref = i; +} \ No newline at end of file diff --git a/Chapter04/lvalues.cpp b/Chapter04/lvalues.cpp new file mode 100644 index 0000000..a20cdb1 --- /dev/null +++ b/Chapter04/lvalues.cpp @@ -0,0 +1,10 @@ +// lvalues.cpp + +int main() +{ + int i; + int j = 10; // "int j" is an lvalue + i = 15; // "i" is an lvalue + + 15 = 10; // error: 15 is NOT an lvalue! +} diff --git a/Chapter04/parameter_passing.cpp b/Chapter04/parameter_passing.cpp new file mode 100644 index 0000000..27c7c16 --- /dev/null +++ b/Chapter04/parameter_passing.cpp @@ -0,0 +1,19 @@ +// parameter_passing.cpp + +void byvalue(int i) +{ + i += 1; +} +void byref(int& i) +{ + i += 1; +} +int main() +{ + int j = 10; + System::Console::WriteLine("Original value: " + j); + byvalue(j); + System::Console::WriteLine("After byvalue: " + j); + byref(j); + System::Console::WriteLine("After byref: " + j); +} diff --git a/Chapter04/pass_by_ref.cpp b/Chapter04/pass_by_ref.cpp new file mode 100644 index 0000000..19cd85e --- /dev/null +++ b/Chapter04/pass_by_ref.cpp @@ -0,0 +1,26 @@ +// pass_by_ref.cpp +using namespace System; + +value class Pair +{ + public: + int x; + int y; +}; + +void swap_values(Pair% pair) +{ + int temp = pair.x; + pair.x = pair.y; + pair.y = temp; +} + +int main() +{ + Pair p; + p.x = 5; + p.y = 3; + Console::WriteLine("{0} {1}", p.x, p.y); + swap_values(p); + Console::WriteLine("{0} {1}", p.x, p.y); +} diff --git a/Chapter04/pass_by_ref2.cpp b/Chapter04/pass_by_ref2.cpp new file mode 100644 index 0000000..f42601c --- /dev/null +++ b/Chapter04/pass_by_ref2.cpp @@ -0,0 +1,29 @@ +// pass_by_ref2.cpp +// This example illustrates that a stack semantics +// reference type can't be redirected by a function +// that operates on references to handles. +using namespace System; + +ref struct R +{ + property int A; + R(int a) { this->A = a; } +}; + +// Takes a reference to a handle +void reset_handle(R^% r) +{ + r = gcnew R(5); +} + +int main() +{ + R r(2); // stack semantics + reset_handle(%r); // use % to create a handle + + // The output is 2, since the handle passed to f + // was a temporary one, so it didn't get changed by + // the function f. + + Console::WriteLine("Value: {0}", r.A); +} diff --git a/Chapter04/pass_by_ref3.cpp b/Chapter04/pass_by_ref3.cpp new file mode 100644 index 0000000..2d23dd6 --- /dev/null +++ b/Chapter04/pass_by_ref3.cpp @@ -0,0 +1,35 @@ +// pass_by_ref3.cpp +// This example requires some careful thought. +// Can you figure out what the final output +// will be? +using namespace System; + +ref struct R +{ + property int A; + R(int a) { this->A = a; } +}; + +// Takes a reference to a handle. This function +// sets the property value on the object, then +// overwrites the object! +// Will the object in the calling scope +// have the value 3, or 5, or will it retain its +// original value? +void reset_handle(R^% r) +{ + r->A = 3; + r = gcnew R(5); +} + +int main() +{ + R r_stack(1); // stack semantics + R^ r_heap = gcnew R(2); // heap semantics + + reset_handle(%r_stack); // use % to create a handle + reset_handle(r_heap); + + Console::WriteLine("Final value, stack semantics: {0}", r_stack.A); + Console::WriteLine("Final value, heap semantics: {0}", r_heap->A); +} diff --git a/Chapter04/pass_by_value.cpp b/Chapter04/pass_by_value.cpp new file mode 100644 index 0000000..cd5d724 --- /dev/null +++ b/Chapter04/pass_by_value.cpp @@ -0,0 +1,38 @@ +// pass_by_value.cpp +using namespace System; + +ref struct R +{ + R() + { + val = 1; + } + + // copy constructor + R( R% r) + { + val = r.val; + } + + property int val; +}; + +// R passed by value (no ^) +void f(R r_local) +{ + // modify r without affecting outer scope + r_local.val = 2; + Console::WriteLine("Within f: " + r_local.val); +} + +int main() +{ + R r; + f(r); + Console::WriteLine("Outside f: " + r.val); + + // The same code, using heap semantics + R^ rhat = gcnew R(); + f(*rhat); // dereference the handle + Console::WriteLine("Outside f: " + rhat->val); +} diff --git a/Chapter04/passing_reference.cpp b/Chapter04/passing_reference.cpp new file mode 100644 index 0000000..5214716 --- /dev/null +++ b/Chapter04/passing_reference.cpp @@ -0,0 +1,5 @@ +// passing_reference.cpp +void increment(int& i) +{ + i++; +} diff --git a/Chapter04/passing_with_copy_ctor.cpp b/Chapter04/passing_with_copy_ctor.cpp new file mode 100644 index 0000000..00cd504 --- /dev/null +++ b/Chapter04/passing_with_copy_ctor.cpp @@ -0,0 +1,35 @@ +// passing_with_copy_ctor.cpp +using namespace System; + +ref class R +{ + int val; + String^ str; + + public: + + property int Value + { + int get() { return val; } + void set(int i) { val = i; } + } + property String^ Label + { + String^ get() { return str; } + void set(String^ s) { str = s; } + } + + R(int val_in, String^ label) : val(val), str(label) + { } + + R(const R% r) + { + // Copy the elements of R. + // Value is a value type, so it gets copied. + val = r.val; + // Strings in C++/CLI are immutable, so even though this does not copy the object + // (it only copies a reference to the object), this is OK here + str = r.str; + } +}; + diff --git a/Chapter04/return_values.cpp b/Chapter04/return_values.cpp new file mode 100644 index 0000000..b82aa69 --- /dev/null +++ b/Chapter04/return_values.cpp @@ -0,0 +1,101 @@ +// return_values.cpp +using namespace System; + +ref class R +{ + bool destroyed; + public: + R() { } + R(const R% r) { } // copy constructor + + R% GetTrackingRefMF(); + void PrintR() + { + if (destroyed) + Console::WriteLine("Using destroyed object!"); + else + Console::WriteLine("R"); + } + + ~R() { destroyed = true; } +}; + +value struct V +{ + int a; + int b; +}; + +// Handle return value: OK +R^ GetHandle() +{ + // create a new R + R^ r = gcnew R(); + // return it + return r; +} + +// Return reference to local variable +// -- avoid +R% GetTrackingRef() +{ + // create a new R + R^ r = gcnew R(); + return *r; // compiler warning +} + +// Return reference to local variable +// -- avoid +R% GetTrackingRef_Bad() +{ + R r; + return r; // compiler warning +} + +// OK: return a nontemporary reference +R% R::GetTrackingRefMF() +{ + return *this; +} + +// Value type return value: OK +V GetValue() +{ + V v; + v.a = 100; + v.b = 54; + // value gets copied + return v; +} + +// Return value with stack semantics +// Requires copy constructor +R GetR() +{ + R r; + return r; // requires copy constructor +} + + +int main() +{ + // Valid uses: + R^ r1 = GetHandle(); // OK + R% r2 = r1->GetTrackingRefMF(); // OK + V v1 = GetValue(); // OK + Console::WriteLine("{0} {1}", v1.a, v1.b); + + R r3 = GetR(); // OK only if R has a copy constructor + + // Using a tracking reference in the GetTrackingRef function works + // but a handle would work as well and would eliminate the compiler + // warning in the function declaration + R% r4 = GetTrackingRef(); + r4.PrintR(); + + // Using the tracking reference here is not OK + // since the destructor was called. + R% r5 = GetTrackingRef_Bad(); + r5.PrintR(); + +} diff --git a/Chapter04/stack_semantics.cpp b/Chapter04/stack_semantics.cpp new file mode 100644 index 0000000..36ed92d --- /dev/null +++ b/Chapter04/stack_semantics.cpp @@ -0,0 +1,13 @@ +int j = 4 ; +void f( int *& pi ) +{ + // the pointer is redirected to a different variable + pi = &j ; +} + +int main() { + int i = 2 ; + int *p = &i; + f( p ); + // i is still 2 here; it was not affected by the function call +} diff --git a/Chapter04/string_array_stack_semantics.cpp b/Chapter04/string_array_stack_semantics.cpp new file mode 100644 index 0000000..aff249d --- /dev/null +++ b/Chapter04/string_array_stack_semantics.cpp @@ -0,0 +1,9 @@ +// string_array_stack_semantics.cpp + +using namespace System; + +int main() +{ + String s = "test"; // error + array a; // error +} diff --git a/Chapter04/stringcopy.cpp b/Chapter04/stringcopy.cpp new file mode 100644 index 0000000..5184d67 --- /dev/null +++ b/Chapter04/stringcopy.cpp @@ -0,0 +1,4 @@ +void stringcopy(char* dest, char* src) +{ + while (*dest++ = *src++); +} diff --git a/Chapter04/unboxing.cpp b/Chapter04/unboxing.cpp new file mode 100644 index 0000000..2db7feb --- /dev/null +++ b/Chapter04/unboxing.cpp @@ -0,0 +1,15 @@ +// unboxing.cpp +using namespace System; + +Object^ f(Object^ obj) +{ + Console::WriteLine("In f, with " + obj->ToString() + "."); + return obj; +} + +int main() +{ + int i = 1; + int j = safe_cast( f(i) ); // cast back to int to unbox the object + +} diff --git a/Chapter04/valuetype_trackingref.cpp b/Chapter04/valuetype_trackingref.cpp new file mode 100644 index 0000000..35b8b72 --- /dev/null +++ b/Chapter04/valuetype_trackingref.cpp @@ -0,0 +1,23 @@ +// valuetype_trackingref.cpp +using namespace System; + +value struct V +{ + int a; + int b; +}; + +void f(V% v) +{ + v.a = 10; + v.b = 20; +} + +int main() +{ + V v; + v.a = 1; + v.b = 2; + f(v); + Console::WriteLine("{0} {1}", v.a, v.b); +} diff --git a/Chapter05/array_dimension_type.cpp b/Chapter05/array_dimension_type.cpp new file mode 100644 index 0000000..abd58f4 --- /dev/null +++ b/Chapter05/array_dimension_type.cpp @@ -0,0 +1,16 @@ +// array_dimension_type.cpp + +void f(int a[][2][3]) { } + +void g(int a[5][2][3]) { } + +int main() +{ + int native_3d_array[5][2][3]; + int native_3d_array2[15][2][3]; + + f(native_3d_array); + f(native_3d_array2); + g(native_3d_array); + g(native_3d_array2); +} diff --git a/Chapter05/array_equality_test.cpp b/Chapter05/array_equality_test.cpp new file mode 100644 index 0000000..00f2cb1 --- /dev/null +++ b/Chapter05/array_equality_test.cpp @@ -0,0 +1,38 @@ +// array_equality_test.cpp +using namespace System; + +// This function tests the equality of two 1D +// arrays of int. +bool ReallyEquals(array^ a, array^ b) +{ + if (a->Length != b->Length) + return false; + + // element-by-element comparison + for (int i = 0; i < a->Length; i++) + { + if (a[i] != b[i]) return false; + } + return true; +} + +int main() +{ + array^ ai1 = gcnew array { 1, 2 }; + array^ ai2 = gcnew array { 1, 2 }; + + // are these arrays equal? + if ( ai1 == ai2 ) + { + Console::WriteLine("The arrays are equal using the == operator."); + } + if (ai1->Equals(ai2) ) + { + Console::WriteLine("The arrays are equal using the Equals method."); + } + if (ReallyEquals(ai1, ai2)) + { + Console::WriteLine( + "The arrays are equal using element-by-element comparison."); + } + } diff --git a/Chapter05/array_exception.cpp b/Chapter05/array_exception.cpp new file mode 100644 index 0000000..97dde77 --- /dev/null +++ b/Chapter05/array_exception.cpp @@ -0,0 +1,17 @@ +// array_exception.cpp +using namespace System; + +int main() +{ + int i; + array^ array1 = { 0, 1, 2}; + + try + { + i = array1[3]; + } + catch(IndexOutOfRangeException^ e) + { + Console::WriteLine( "{0}, {1}" , e->ToString(), e->Message); + } +} diff --git a/Chapter05/arraylist.cpp b/Chapter05/arraylist.cpp new file mode 100644 index 0000000..8a269fd --- /dev/null +++ b/Chapter05/arraylist.cpp @@ -0,0 +1,24 @@ +// arraylist.cpp +using namespace System; +using namespace System::Collections; + +int main() +{ + ArrayList^ array_list = gcnew ArrayList(); + + array_list->Add("apple"); + array_list->Add("banana"); + + // iterate using the for each operator + for each (String^ s in array_list) + { + Console::WriteLine( s ); + } + + // iterate using indexing + + for (int i = 0; i < array_list->Count; i++) + { + Console::WriteLine("{0} {1}", i, array_list[i]); + } +} diff --git a/Chapter05/arrays_copy.cpp b/Chapter05/arrays_copy.cpp new file mode 100644 index 0000000..81dc430 --- /dev/null +++ b/Chapter05/arrays_copy.cpp @@ -0,0 +1,24 @@ +// arrays_copy.cpp +using namespace System; + +int main() +{ + array^ array1 = { 0, 1, 2}; + + // shallow copy creates another name for the array + array^ array2 = array1; + + array2[0] = 100; + + // This prints "100" since array2 is a synonym of array1 + Console::WriteLine( "{0}", array1[0] ); + + array^ array3 = gcnew array(3); + Array::Copy(array1, array3, array1->Length); + + // Change a value in the new copy of the array + array3[0] = 200; + + // This prints "100 1 2" since the old array was not affected + Console::WriteLine( "{0} {1} {2}", array1[0], array1[1], array1[2]); +} diff --git a/Chapter05/arrays_foreach.cpp b/Chapter05/arrays_foreach.cpp new file mode 100644 index 0000000..882fa9c --- /dev/null +++ b/Chapter05/arrays_foreach.cpp @@ -0,0 +1,13 @@ +// arrays_foreach.cpp +using namespace System; + +int main() +{ + array^ stringArray = gcnew array + { "one", "two", "three", "four", "five" }; + + for each (String^ str in stringArray) + { + Console::WriteLine(str); + } +} diff --git a/Chapter05/arrays_initializing.cpp b/Chapter05/arrays_initializing.cpp new file mode 100644 index 0000000..5c01ccf --- /dev/null +++ b/Chapter05/arrays_initializing.cpp @@ -0,0 +1,18 @@ +// arrays_initializing.cpp +int main() +{ + + // declare, create, and initialize a 1D native array + int native_array[2] = { 10, 20 }; + + // declare, create, and initialize a 1D managed array + array^ managed_array = gcnew array(2) { 10, 20 }; + + // declare, create, and initialize a 2D native array + int native_array_2D[2][2] = { { 1, 0 }, { 0, 1 } }; + + // declare, create, and initialize a 2D managed array + array^ managed_array_2D = gcnew array(2, 2) + { { 1, 0 }, { 0, 1 } }; + +} diff --git a/Chapter05/arrays_initializing2.cpp b/Chapter05/arrays_initializing2.cpp new file mode 100644 index 0000000..2535e04 --- /dev/null +++ b/Chapter05/arrays_initializing2.cpp @@ -0,0 +1,17 @@ +// arrays_initializing2.cpp +int main() +{ + // initialization without gcnew + + array^ array_int1 = { 0, 1, 2 }; + + // initialization with gcnew (no equal sign is used) + // here, the size is omitted and determined by the three + // elements in the initializer list + array^ array_int2 = gcnew array { 0, 1, 2 }; + + // You can use variables in the initializer list + + int i = 1, j = 2, k = 3; + array^ array_int3 = { i, j, k }; +} diff --git a/Chapter05/arrays_interior_ptr.cpp b/Chapter05/arrays_interior_ptr.cpp new file mode 100644 index 0000000..60a4648 --- /dev/null +++ b/Chapter05/arrays_interior_ptr.cpp @@ -0,0 +1,30 @@ +// arrays_interior_ptr.cpp +using namespace System; + +ref class Buf +{ + // ... +}; + +int main() +{ + array^ array_of_buf = gcnew array(10); + + // Create a Buf object for each array position + for (int i = 0; i < array_of_buf->Length; i++) + { + array_of_buf[i] = gcnew Buf(); + } + + // create an interior pointer to elements of the array + interior_ptr ptr_buf; + + // loop over the array with the interior pointer + // using pointer arithmetic on the interior pointer + for (ptr_buf = &array_of_buf[0]; ptr_buf <= &array_of_buf[9]; ptr_buf++) + { + // dereference the interior pointer with * + Buf^ buf = *ptr_buf; + // use the Buf class + } +} diff --git a/Chapter05/arrays_iterators.cpp b/Chapter05/arrays_iterators.cpp new file mode 100644 index 0000000..bf26209 --- /dev/null +++ b/Chapter05/arrays_iterators.cpp @@ -0,0 +1,19 @@ +// arrays_iterators.cpp +using namespace System; +using namespace System::Collections; + +int main() +{ + array^ dateArray = gcnew array(2); + + dateArray[0] = gcnew DateTime(1970, 12, 18); + dateArray[1] = gcnew DateTime(1990, 1, 5); + + IEnumerator^ enumerator1 = dateArray->GetEnumerator(); + while ( enumerator1->MoveNext() ) + { + DateTime^ current = (DateTime^) enumerator1->Current; + Console::WriteLine( current->ToString("MM/dd/yyyy") ); + } + +} diff --git a/Chapter05/arrays_length.cpp b/Chapter05/arrays_length.cpp new file mode 100644 index 0000000..0d87eeb --- /dev/null +++ b/Chapter05/arrays_length.cpp @@ -0,0 +1,13 @@ +// arrays_length.cpp +using namespace System; + +int main() +{ + array^ string_array = + gcnew array(2) { "first", "second" } ; + + for (int i = 0; i < string_array->Length; i++) + { + Console::WriteLine( string_array[i] ); + } +} diff --git a/Chapter05/arrays_managed_native_comparison.cpp b/Chapter05/arrays_managed_native_comparison.cpp new file mode 100644 index 0000000..70aef48 --- /dev/null +++ b/Chapter05/arrays_managed_native_comparison.cpp @@ -0,0 +1,22 @@ +// arrays_managed_native_comparison.cpp +#include +using namespace std; + +int main() +{ + // native 1D array + int native_array_1D[10]; + + // managed 1D array + array^ managed_array_1D = gcnew array(10); + + for (int i = 0; i < 10; i++) + { + native_array_1D[i] = i*i; + cout << native_array_1D[i] << " "; + managed_array_1D[i] = native_array_1D[i]; + cout << managed_array_1D[i] << " "; + } + + cout << endl; +} diff --git a/Chapter05/arrays_nondefault_ctor.cpp b/Chapter05/arrays_nondefault_ctor.cpp new file mode 100644 index 0000000..d93bb30 --- /dev/null +++ b/Chapter05/arrays_nondefault_ctor.cpp @@ -0,0 +1,19 @@ +// arrays_nondefault_ctor.cpp +using namespace System; + +ref class C +{ + + public: + C(int i) { Value = i; } + + property int Value; +}; + +int main() +{ + array^ array_C = { gcnew C(0), gcnew C(1), gcnew C(2)}; + + Console::WriteLine( " {0}, {1}, {2} ", array_C[0]->Value, + array_C[1]->Value, array_C[2]->Value); +} diff --git a/Chapter05/arrays_parameter.cpp b/Chapter05/arrays_parameter.cpp new file mode 100644 index 0000000..5ba586f --- /dev/null +++ b/Chapter05/arrays_parameter.cpp @@ -0,0 +1,21 @@ +// arrays_parameter.cpp +using namespace System; + +// using an array as an argument + +void set_to_one(int i, array^ array_arg) +{ + // change the array inside this function + array_arg[i] = 1; +} + +int main() +{ + array^ array1 = { 0, 1 }; + set_to_one(0, array1); + + // The output here is " 1 1", indicating that the array + // change is made to the same array + Console::WriteLine(" {0} {1}", array1[0], array1[1]); + +} diff --git a/Chapter05/arrays_sort_search.cpp b/Chapter05/arrays_sort_search.cpp new file mode 100644 index 0000000..4716e5c --- /dev/null +++ b/Chapter05/arrays_sort_search.cpp @@ -0,0 +1,26 @@ +// arrays_sort_search.cpp +using namespace System; + +int main() +{ + array^ array1 = gcnew array(10) + { 122, 87, 99, 6, 45, 12, 987, 115, 0, 10 }; + + Array::Sort(array1); + + for each (int i in array1) + { + // Output is sorted + Console::Write("{0} ", i); + } + + Console::WriteLine(); + + // Search for one of the values + int index = Array::BinarySearch( array1, 115); + + if (index >= 0 ) + Console::WriteLine( "Found {0} at position {1}.", array1[index], index ); + else + Console::WriteLine(" Not Found. "); +} diff --git a/Chapter05/arrays_uninitialized_elements.cpp b/Chapter05/arrays_uninitialized_elements.cpp new file mode 100644 index 0000000..ea3ee72 --- /dev/null +++ b/Chapter05/arrays_uninitialized_elements.cpp @@ -0,0 +1,15 @@ +// arrays_uninitialized_elements.cpp +using namespace System; + +int main() +{ + array^ stringArray = gcnew array(5) + { "one", "two" }; + + for (int i = 0; i < stringArray->Length; i++) + { + Console::WriteLine( stringArray[i] ); + } + + Console::WriteLine("End."); +} diff --git a/Chapter05/cli_printf.cpp b/Chapter05/cli_printf.cpp new file mode 100644 index 0000000..956229d --- /dev/null +++ b/Chapter05/cli_printf.cpp @@ -0,0 +1,13 @@ +// cli_printf.cpp + +using namespace System; +#include + +int main() +{ + String^ str = "managed string"; + + // The string is automatically converted to a + // char array for printf_s + printf_s("%s", str ); +} diff --git a/Chapter05/convert_and_parse.cpp b/Chapter05/convert_and_parse.cpp new file mode 100644 index 0000000..b6829d4 --- /dev/null +++ b/Chapter05/convert_and_parse.cpp @@ -0,0 +1,30 @@ +// convert_and_parse.cpp + +using namespace System; + +int main() +{ + String^ str1 = "115"; + String^ str2 = "1.4e-12"; + + // parse the string to get the integer value + int i = Int32::Parse( str1 ); + + // get the double value + double x = Double::Parse( str2 ); + + // use Convert class to convert the value + int j = Convert::ToInt32( str1 ); + double y = Convert::ToDouble( str2 ); + + // exception handlers may be used to catch parse failures and overflows + + try + { + int k = Int32::Parse("bad format"); + } + catch(FormatException^ e) + { + Console::WriteLine("Exception occurred! {0}", e->Message ); + } +} diff --git a/Chapter05/enum.cpp b/Chapter05/enum.cpp new file mode 100644 index 0000000..77b78cc --- /dev/null +++ b/Chapter05/enum.cpp @@ -0,0 +1,20 @@ +// enum.cpp + +enum class Flavor +{ + Vanilla, + Chocolate, + Strawberry +}; + +int main() +{ + // The enum variable may be a handle + // or a stack variable. If used as a handle, + // it's a boxed value type. + + // The enum value, Vanilla, is + // scoped by the enum class name + Flavor^ flavor_handle = Flavor::Vanilla; + Flavor flavor_stack = Flavor::Vanilla; +} diff --git a/Chapter05/enum_flags.cpp b/Chapter05/enum_flags.cpp new file mode 100644 index 0000000..2fb227a --- /dev/null +++ b/Chapter05/enum_flags.cpp @@ -0,0 +1,55 @@ +// enum_flags.cpp +using namespace System; + +[ Flags ] +enum class FontFormat +{ + None = 0, // No flags set. + BOLD = 1, // the values are set to powers of 2 + ITALIC = 2, // so that in binary, each represents one bit position + UNDERLINE = 4, + STRIKETHROUGH = 8, + RED = 16, + FLASHING = 32, + BOLD_ITALIC = BOLD | ITALIC // combination of two values +}; + +ref class Font +{ + public: + + property String^ Name; + + Font(String^ s) { Name = s; } +}; + +ref class Display +{ + public: + + static void SetFont(Font^ font, FontFormat format) + { + // Testing the bits of a Flags enum using the bitwise and operator (&) + // requires a cast to int. + if (safe_cast(format) & safe_cast(FontFormat::BOLD)) + { + // use a bold font + } + if (safe_cast(format) & safe_cast(FontFormat::ITALIC)) + { + // use italics + } + // etc. + }; + +}; + +int main() +{ + // The bitwise or operator (|) combines the flag values + Display::SetFont(gcnew Font("Times New Roman"), + FontFormat::BOLD | FontFormat::RED ); + + Display::SetFont(gcnew Font("Helvetica"), + FontFormat::ITALIC | FontFormat::FLASHING ); +} diff --git a/Chapter05/enum_format.cpp b/Chapter05/enum_format.cpp new file mode 100644 index 0000000..9ef9e9e --- /dev/null +++ b/Chapter05/enum_format.cpp @@ -0,0 +1,27 @@ +// enum_format.cpp +using namespace System; + +enum class Color +{ + Red = 1, + Blue = 2, + Green = 3 +}; + +int main() +{ + Console::WriteLine("Colors: {0}, {1}, {2}", Color::Red, Color::Blue, + Color::Green); + Console::WriteLine("Colors: {0:d}, {1:d}, {2:d}", Color::Red, Color::Blue, + Color::Green); + + Color c = Color::Red; + + String^ s1 = c.ToString("X"); // specify the hex representation + Console::WriteLine( s1 ); + + // Use the Format method of the Enum class + String^ s2 = Enum::Format( Color::typeid, c , "G"); + + Console::WriteLine(s2 ); +} diff --git a/Chapter05/enum_format2.cpp b/Chapter05/enum_format2.cpp new file mode 100644 index 0000000..b7baa58 --- /dev/null +++ b/Chapter05/enum_format2.cpp @@ -0,0 +1,30 @@ +// enum_format2.cpp +using namespace System; + +// Use the FlagsAttribute +[ Flags ] +enum class Color +{ + Red = 1, + Blue = 2, + Green = 4 // use powers of 2 +}; + +int main() +{ + Console::WriteLine("Colors: {0}, {1}, {2}", Color::Red, Color::Blue, + Color::Green); + Console::WriteLine("Colors: {0:d}, {1:d}, {2:d}", Color::Red, Color::Blue, + Color::Green); + + // Use the bitwise OR operator to combine flags. + Color c = Color::Red | Color::Blue; + + String^ s1 = c.ToString("X"); // specify the hex representation + Console::WriteLine( s1 ); + + // Use the Format method of the Enum class + String^ s2 = Enum::Format( Color::typeid, c , "G"); + + Console::WriteLine(s2 ); +} diff --git a/Chapter05/enum_type_specified.cpp b/Chapter05/enum_type_specified.cpp new file mode 100644 index 0000000..8e246f0 --- /dev/null +++ b/Chapter05/enum_type_specified.cpp @@ -0,0 +1,17 @@ +// enum_type_specified.cpp +using namespace System; + +enum class Ordinal : char +{ + zero, one, two, three, four, five, six, seven, eight, nine, ten, + eleven, twelve, thirteen, fourteen, fifteen, sixteen, seventeen, + eighteen, nineteen, twenty +}; + +int main() +{ + char c1 = 13; + char c2 = 156; + Ordinal ord1 = safe_cast(c1); + Console::WriteLine(ord1.ToString()); +} diff --git a/Chapter05/exception_message.cpp b/Chapter05/exception_message.cpp new file mode 100644 index 0000000..014a607 --- /dev/null +++ b/Chapter05/exception_message.cpp @@ -0,0 +1,26 @@ + +using namespace System; +using namespace System::IO; + +int main() +{ + + String^ filename = "textfile.txt"; + try + { + // Another way of creating a StreamReader class is with + // static methods of the File class. + StreamReader^ sr2 = File::OpenText(filename); + + String^ line; + // Read each line and write it out to the console. + while ((line = sr2->ReadLine()) != nullptr) + { + Console::WriteLine(line); + } + } + catch(IOException^ e) + { + Console::WriteLine("Exception! {0}", e->Message ); + } +} diff --git a/Chapter05/list_generic.cpp b/Chapter05/list_generic.cpp new file mode 100644 index 0000000..5d69dc3 --- /dev/null +++ b/Chapter05/list_generic.cpp @@ -0,0 +1,24 @@ +// list_generic.cpp +using namespace System; +using namespace System::Collections::Generic; + +int main() +{ + List^ list = gcnew List(); + + list->Add("apple"); + list->Add("banana"); + + // iterate using the for each operator + for each (String^ s in list) + { + Console::WriteLine( s ); + } + + // iterate using indexing + + for (int i = 0; i < list->Count; i++) + { + Console::WriteLine("{0} {1}", i, list[i]); + } +} diff --git a/Chapter05/managed_multidimensional_array_as_parameter.cpp b/Chapter05/managed_multidimensional_array_as_parameter.cpp new file mode 100644 index 0000000..53912d2 --- /dev/null +++ b/Chapter05/managed_multidimensional_array_as_parameter.cpp @@ -0,0 +1,16 @@ +// managed_multidimensional_array_as_parameter.cpp + +// g takes an array of 5 arrays 2 x 3 in size +void g(array^ a) +{ + +} + +int main() +{ + array^ managed_3d_array1 = gcnew array(5, 2, 3); + array^ managed_3d_array2 = gcnew array(15, 2, 3); + + g(managed_3d_array1); + g(managed_3d_array2); +} diff --git a/Chapter05/param_array.cpp b/Chapter05/param_array.cpp new file mode 100644 index 0000000..f2a3911 --- /dev/null +++ b/Chapter05/param_array.cpp @@ -0,0 +1,24 @@ +// param_array.cpp +using namespace System; + +// Total takes at least one int and a variable +// number of subsequent integers that are wrapped +// into a managed array +int Total( int a, ... array^ varargs) +{ + int tot = a; + for each ( int i in varargs) + { + tot += i; + } + return tot; +} + +int main() +{ + int sum1 = Total(100, 200, 350); + Console::WriteLine("First total: {0}", sum1); + + int sum2 = Total(1, 2, 3, 4, 5, 6, 7, 8); + Console::WriteLine("Second total: {0}", sum2); +} diff --git a/Chapter05/stream_writer_reader.cpp b/Chapter05/stream_writer_reader.cpp new file mode 100644 index 0000000..1d46fea --- /dev/null +++ b/Chapter05/stream_writer_reader.cpp @@ -0,0 +1,25 @@ +// stream_writer_reader.cpp + +using namespace System; +using namespace System::IO; + +int main() +{ + + StreamWriter^ sw = gcnew StreamWriter("textfile.txt"); + sw->WriteLine("Can code be poetry?"); + sw->Flush(); + sw->Close(); + + // The File class's CreateText static method is used to + // create a text file + StreamWriter^ sw2 = File::CreateText("newtextfile.txt"); + + StreamReader^ sr = gcnew StreamReader("textfile.txt"); + String^ line; + // Read each line and write it out to the console. + while ((line = sr->ReadLine()) != nullptr) + { + Console::WriteLine(line); + } +} \ No newline at end of file diff --git a/Chapter05/string_alignment_specifier.cpp b/Chapter05/string_alignment_specifier.cpp new file mode 100644 index 0000000..fb63395 --- /dev/null +++ b/Chapter05/string_alignment_specifier.cpp @@ -0,0 +1,18 @@ +// string_alignment_specifier.cpp +using namespace System; + +int main() +{ + // The format string is interpreted as follows: + // { 0, -30 } 30 characters in width, left-justified + // { 1, 10 } 10 characters in width, right-justified + // { 2, 10:c2 } 10 characters in width, currency with 2 decimal places + String^ format = "{0,-30}{1,10}{2,10:c2}"; + String^ header = String::Format(format, "Item", "Quantity", "Price"); + String^ str1 = str1->Format(format, "Matches, Strike Anywhere", 10, 0.99); + String^ str2 = str2->Format(format, "Gloves", 1, 12.50); + String^ str3 = str3->Format(format, "Iodine", 1, 4.99); + + Console::WriteLine(header); + Console::WriteLine(str1 + "\n" + str2 + "\n" + str3); +} diff --git a/Chapter05/string_compare.cpp b/Chapter05/string_compare.cpp new file mode 100644 index 0000000..99b7289 --- /dev/null +++ b/Chapter05/string_compare.cpp @@ -0,0 +1,23 @@ +// string_compare.cpp +using namespace System; + +int main() +{ + String^ str1 = "cat"; + String^ str2 = "cab"; + + if (str1->CompareTo( str2 ) < 0) + { + Console::WriteLine(str1 + " is less than " + str2); + } + // For variety, use the static method. + else if ( String::Compare(str1, str2) > 0 ) + { + Console::WriteLine("{0} is less than {1}", str2, str1); + } + + else if ( str1->CompareTo( str2 ) == 0) + { + Console::WriteLine("The strings are both equal, with value {0}.", str1); + } +} diff --git a/Chapter05/string_equality.cpp b/Chapter05/string_equality.cpp new file mode 100644 index 0000000..ea9ba83 --- /dev/null +++ b/Chapter05/string_equality.cpp @@ -0,0 +1,42 @@ +// string_equality.cpp +using namespace System; + +int main() +{ + String^ str1 = "1"; + String^ str2 = "1"; + String^ str3 = str1; + + // All of the following tests result in True, since + // the == operator is equivalent to the Equals method. + if (str1 == str2) + { + Console::WriteLine(" str1 == str2" ); + } + if (str1 == str3) + { + Console::WriteLine(" str1 == str3" ); + } + if (str1->Equals(str2)) + { + Console::WriteLine(" str1 Equals str2" ); + } + if (str1->Equals(str3)) + { + Console::WriteLine(" str1 Equals str3"); + } + + // ReferenceEquals compares the handles, not the actual + // string. The results are implementation dependent, + // since if the compiler creates a single string representation + // for both string literals, as is the case here, this will resolve + // true. + if (String::ReferenceEquals(str1, str2)) + { + Console::WriteLine(" str1 ReferenceEquals str2"); + } + if (String::ReferenceEquals(str1, str3)) + { + Console::WriteLine(" str1 ReferenceEquals str3"); + } +} diff --git a/Chapter05/string_foreach.cpp b/Chapter05/string_foreach.cpp new file mode 100644 index 0000000..2676b96 --- /dev/null +++ b/Chapter05/string_foreach.cpp @@ -0,0 +1,13 @@ +// string_foreach.cpp +using namespace System; + +int main() +{ + String^ str1 = "Ode on a Grecian Urn"; + + for each (Char ch in str1) + { + Console::Write(ch); + } + Console::WriteLine(); +} diff --git a/Chapter05/string_numerical_formatting.cpp b/Chapter05/string_numerical_formatting.cpp new file mode 100644 index 0000000..ff4f56b --- /dev/null +++ b/Chapter05/string_numerical_formatting.cpp @@ -0,0 +1,46 @@ +// string_numerical_formatting.cpp +using namespace System; + +int main() +{ + String^ str; + int i = -73000; + double dbl = 1005.01; + + // Formats for floating-point types: + + str = String::Format("Currency format: {0:c2}", dbl); + Console::WriteLine(str); + + str = String::Format("Scientific format: {0:e6}", dbl); + Console::WriteLine(str); + + str = String::Format("Fixed-point format: {0:f6}", dbl); + Console::WriteLine(str); + + str = String::Format("General format: {0:g6}", dbl); + Console::WriteLine(str); + + str = String::Format("Number format: {0:n6}", dbl); + Console::WriteLine(str); + + str = String::Format("Percent format: {0:p6}", dbl); + Console::WriteLine(str); + + str = String::Format("Round-trip format: {0:r6}", dbl); + Console::WriteLine(str); + + // Formats for integral types: + + str = String::Format("Decimal format: {0:d6}", i); + Console::WriteLine(str); + + str = String::Format("General format: {0:g6}", i); + Console::WriteLine(str); + + str = String::Format("Number format: {0:n0}", i); + Console::WriteLine(str); + + str = String::Format("Hexadecimal format: {0:x8}", i); + Console::WriteLine(str); +} diff --git a/Chapter05/string_operator_plus.cpp b/Chapter05/string_operator_plus.cpp new file mode 100644 index 0000000..808e6a3 --- /dev/null +++ b/Chapter05/string_operator_plus.cpp @@ -0,0 +1,12 @@ +// string_operator_plus.cpp +using namespace System; + +int main() +{ + String ^hrs = "Hours", ^mins = "Minutes"; + wchar_t separator = ':'; + int minutes = 56, hours = 1; + + Console::WriteLine( hrs + separator + " " + hours + "\n" + mins + + separator + " " + minutes); +} diff --git a/Chapter05/string_tochararray.cpp b/Chapter05/string_tochararray.cpp new file mode 100644 index 0000000..742ad02 --- /dev/null +++ b/Chapter05/string_tochararray.cpp @@ -0,0 +1,29 @@ +// string_tochararray.cpp +using namespace System; + +int main() +{ + String^ str = "A quick sly fox jumped over the lazy brown dog."; + + array^ character_array = str->ToCharArray(); + + // Prints the original string. + Console::WriteLine( str); + + // Modify characters in the character array. + for (int i = 0; i < character_array->Length; i++) + { + if ( character_array[i] >= L'a' && character_array[i] <= 'z') + { + character_array[i] -= (L'a' - L'A'); + } + } + + // Convert back to a String using the String constructor + // that takes a Unicode character array. + str = gcnew String(character_array); + + // Prints the modified string: + // A QUICK SLY FOX JUMPED OVER THE LAZY BROWN DOG. + Console::WriteLine( str); +} diff --git a/Chapter05/string_wcout.cpp b/Chapter05/string_wcout.cpp new file mode 100644 index 0000000..a016ead --- /dev/null +++ b/Chapter05/string_wcout.cpp @@ -0,0 +1,14 @@ +// string_wcout.cpp +#include +#include + +using namespace std; +using namespace System; + +int main() +{ + String^ s = "Testing String conversion to iostream."; + + pin_ptr ptr = PtrToStringChars(s); + wcout << static_cast( ptr ) << endl; +} diff --git a/Chapter05/stringbuilder.cpp b/Chapter05/stringbuilder.cpp new file mode 100644 index 0000000..0345c0e --- /dev/null +++ b/Chapter05/stringbuilder.cpp @@ -0,0 +1,21 @@ +// stringbuilder.cpp + +using namespace System; +using namespace System::Text; + +int main() +{ + // Construct a StringBuilder string with initial contents + // "C" and initial capacity 30. + StringBuilder^ sb = gcnew StringBuilder("C", 30); + + sb->Append(gcnew array{'+','+'}); + + sb->Append("/CLI."); + + sb->Insert(0, "I love "); + + sb->Replace(".","!"); + + Console::WriteLine( sb->ToString() ); +} diff --git a/Chapter05/stringwriter.cpp b/Chapter05/stringwriter.cpp new file mode 100644 index 0000000..06f789c --- /dev/null +++ b/Chapter05/stringwriter.cpp @@ -0,0 +1,75 @@ +// stringwriter.cpp + +// The Windows Forms namespace lives in a different +// assembly, which is not referenced by default as is +// mscorlib.dll, so we must use #using here +#using "System.Windows.Forms.dll" + +using namespace System; +using namespace System::IO; +using namespace System::Text; +using namespace System::Windows::Forms; + +int main() +{ + StringWriter^ sw = gcnew StringWriter(); + sw->WriteLine("Pike Place"); + sw->WriteLine("Street of Dreams"); + sw->WriteLine("(C) 2006 Jeni Hogenson"); + sw->WriteLine(); + + sw->Write("Walking with bare feet\n"); + sw->Write("Seattle streets, gospel beat,\n"); + sw->Write("She's got magic\n"); + sw->WriteLine(); + + sw->WriteLine("Bag of black upon her back\n" + + "A sensual blend, soul food that is;\n" + + "Local color."); + sw->WriteLine(); + + String^ jambo = "jambo"; + String^ s = String::Format("Open the bag, {0}, {1}.", jambo, jambo); + sw->WriteLine(s); + sw->Write("Make a wish, {0}, {0}.", jambo); + sw->WriteLine(); + + s = "Feel it, grab it, grope it.\n"; + String::Concat(s, "Follow every curve.\n"); + String::Concat(s, "Can you wait to find it?\n"); + String::Concat(s, "Do you have the nerve?"); + sw->WriteLine(s); + + sw->WriteLine("A drop of oil, jambo, jambo."); + sw->WriteLine("Whisper in her ear,"); + sw->WriteLine("Ask the question in your heart"); + sw->WriteLine("that only you can hear"); + sw->WriteLine(); + + StringBuilder^ sb = gcnew StringBuilder(); + sb->Append("Fingers now upon your ears,\n"); + sb->Append("Waiting for the space\n"); + sb->Append("An answer if you're ready now\n"); + sb->Append("From the marketplace\n"); + sw->WriteLine(sb); + + sw->WriteLine("The call of a bird, jambo, jambo."); + sw->WriteLine("The scent of a market flower,"); + sw->WriteLine("Open wide to all of it and"); + sw->WriteLine("Welcome back your power"); + sw->WriteLine(); + + sw->WriteLine("Jambo this and jambo that,"); + sw->WriteLine("Walking with bare feet."); + sw->WriteLine("No parking allowed when down under,"); + sw->WriteLine("Keep it to the street."); + sw->WriteLine(); + + sw->WriteLine("Dead people rising,"); + sw->WriteLine("Walking with bare feet,"); + sw->WriteLine("No parking allowed when down under,"); + sw->WriteLine("Keep it to the street."); + + // The resulting string might be displayed to the user in a GUI + MessageBox::Show(sw->ToString(), "Poetry", MessageBoxButtons::OK); +} diff --git a/Chapter05/to_upper.cpp b/Chapter05/to_upper.cpp new file mode 100644 index 0000000..e74706a --- /dev/null +++ b/Chapter05/to_upper.cpp @@ -0,0 +1,12 @@ +// to_upper.cpp +// Convert text read from stdin to uppercase and write to stdout +using namespace System; + +int main() +{ + String^ str; + while ((str = Console::ReadLine()) != nullptr) + { + Console::WriteLine( str->ToUpper() ); + } +} diff --git a/Chapter05/writeline.cpp b/Chapter05/writeline.cpp new file mode 100644 index 0000000..50ad37e --- /dev/null +++ b/Chapter05/writeline.cpp @@ -0,0 +1,22 @@ +// writeline.cpp + +using namespace System; + +int main() +{ + // output without newline + Console::Write("a"); + Console::Write("b"); + Console::Write("c"); + + // newline alone + Console::WriteLine(); + + // output with format string + Console::WriteLine("Fourscore and {0} years ago.", 7); + + // output with direct types + Console::WriteLine(7); + Console::WriteLine( 1.05); + Console::WriteLine('A'); +} diff --git a/Chapter06/Forestry.cpp b/Chapter06/Forestry.cpp new file mode 100644 index 0000000..049fbe0 --- /dev/null +++ b/Chapter06/Forestry.cpp @@ -0,0 +1,47 @@ +// Forestry.cpp + +#include + +using namespace System; +using namespace System::Runtime::InteropServices; +using namespace msclr::interop; + +// a native class +class PlantData +{ + private: + + wchar_t* family; + wchar_t* genus; + wchar_t* species; + + public: + + PlantData(const wchar_t* botanical_name) + { + // Let's assume this method + // populates its + // fields with data from the database + } + +}; + +// The following managed class contains a pointer to a native class. + +ref class TreeSpecies +{ + PlantData* treedata; + + public: + TreeSpecies(String^ genus, String^ species) + { + String^ botanicalName = gcnew String(genus + " " + species); + + const wchar_t* str = marshal_as(botanicalName); + treedata = new PlantData(str); + + } + + ~TreeSpecies() { this->!TreeSpecies(); } + !TreeSpecies() { if (treedata) delete treedata; } +}; diff --git a/Chapter06/Forestry_marshal_as.cpp b/Chapter06/Forestry_marshal_as.cpp new file mode 100644 index 0000000..d2d5120 --- /dev/null +++ b/Chapter06/Forestry_marshal_as.cpp @@ -0,0 +1,47 @@ +// Forestry.cpp + +#include + +using namespace System; +using namespace System::Runtime::InteropServices; +using namespace msclr::interop; + +// a native class +class PlantData +{ + private: + + wchar_t* family; + wchar_t* genus; + wchar_t* species; + + public: + + PlantData(const wchar_t* botanical_name) + { + // Let's assume this method + // populates its + // fields with data from the database + } + +}; + +// The following managed class contains a pointer to a native class. + +ref class TreeSpecies +{ + PlantData* treedata; + + public: + TreeSpecies(String^ genus, String^ species) + { + String^ botanicalName = gcnew String(genus + " " + species); + marshal_context context; + const wchar_t* str = context.marshal_as(botanicalName); + treedata = new PlantData(str); + + } + + ~TreeSpecies() { this->!TreeSpecies(); } + !TreeSpecies() { if (treedata) delete treedata; } +}; diff --git a/Chapter06/const_correct.config b/Chapter06/const_correct.config new file mode 100644 index 0000000..28a0c4c --- /dev/null +++ b/Chapter06/const_correct.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/Chapter06/const_correct.cpp b/Chapter06/const_correct.cpp new file mode 100644 index 0000000..01f79a0 --- /dev/null +++ b/Chapter06/const_correct.cpp @@ -0,0 +1,4 @@ +class N +{ + void f() const { /* code which does not modify the object data */} +}; diff --git a/Chapter06/declaring_literals.cpp b/Chapter06/declaring_literals.cpp new file mode 100644 index 0000000..479c36f --- /dev/null +++ b/Chapter06/declaring_literals.cpp @@ -0,0 +1,9 @@ +ref class Scrabble +{ + // literals are constants that can be initialized in the class body + literal int TILE_COUNT = 100; // the number of tiles altogether + literal int TILES_IN_HAND = 7; // the number of tiles in each hand + + // ... + +}; diff --git a/Chapter06/destructor_and_finalizer.cpp b/Chapter06/destructor_and_finalizer.cpp new file mode 100644 index 0000000..93a9d94 --- /dev/null +++ b/Chapter06/destructor_and_finalizer.cpp @@ -0,0 +1,39 @@ +// destructor_and_finalizer.cpp + +ref class ManagedResource +{ + public: + void Free() { /* free resource */ } +}; + +class NativeResource +{ + public: + void Free() { /* free resource */ } +}; + +ref class R +{ + ManagedResource^ resource1; + NativeResource* nativeResource; + + public: + ~R() + { + // you may clean up managed resources that you want to free up promptly + // here. If you don't, they WILL eventually get cleaned up by the garbage + // collector. + // If the destructor is NOT called, the GC will eventually clean + // them up. + resource1->Free(); + this->!R(); + } + !R() + { + // clean up unmanaged resources that the + // garbage collector doesn't know how to clean up. + // That code shouldn't be in the destructor because + // the destructor might not get called + nativeResource->Free(); + } +}; diff --git a/Chapter06/fileconverter.config b/Chapter06/fileconverter.config new file mode 100644 index 0000000..28a0c4c --- /dev/null +++ b/Chapter06/fileconverter.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/Chapter06/fileconverter.cpp b/Chapter06/fileconverter.cpp new file mode 100644 index 0000000..aa03a80 --- /dev/null +++ b/Chapter06/fileconverter.cpp @@ -0,0 +1,149 @@ +// file_converter.cpp +#include +#include +#include +#include // for PtrToStringChars + +using namespace System; +using namespace System::IO; + +// a native class + +class FileNative +{ + // a CRT file pointer + FILE* fp; + + public: + + void Open(const char* filename) + { + int err = fopen_s(&fp, filename, "r"); + if (err) + { + printf("Error opening file %s. Error code %d.\n", filename, err); + } + } + + int Read(char* line) + { + int val = fread(line, 1, 1, fp); + if (feof(fp)) + { + return 0; + } + return val; + } + + void Close() + { + if (fp) + fclose(fp); + } +}; + +// a managed class that contains a managed resource (StreamWriter) +// and a native resource (fileNative, a native class containing a native file). +ref class FileConverter +{ + FileNative* fileNative; + StreamWriter^ sw; + + public: + + FileConverter(String^ source_file) + { + fileNative = new FileNative(); + pin_ptr wfilename = PtrToStringChars(source_file); + size_t convertedChars = 0; + size_t sizeInBytes = ((source_file->Length + 1) * 2); + errno_t err = 0; + char *filename = (char *)malloc(sizeInBytes); + + err = wcstombs_s(&convertedChars, + filename, sizeInBytes, + wfilename, sizeInBytes); + if (err != 0) + printf_s("wcstombs_s failed!\n"); + + fileNative->Open(filename); + } + + void Convert(String^ dest_file) + { + String^ text; + char ptr[1024]; + int len; + try + { + sw = gcnew StreamWriter(dest_file); + } + catch(Exception^ e) + { + Console::WriteLine("Error occurred. {0}", e->Message); + } + while ((len = fileNative->Read(ptr)) != 0) + { + // This version of the string constructor takes + // a char* pointer, an offset, and a number of characters + // to create the String from a portion of a character array. + text = gcnew String(ptr, 0, len); + Console::Write(text); + sw->Write(text); + } + } + + // A way to close the files promptly without waiting + // for the cleanup to occur. + void Close() + { + if (sw != nullptr) + sw->Close(); + fileNative->Close(); + } + + // Destructor: close the managed filestream, and call finalizer. + ~FileConverter() + { + if (sw != nullptr) + sw->Close(); + this->!FileConverter(); + } + + // Finalizer: close the native file handle. + !FileConverter() + { + fileNative->Close(); + } + +}; + + +int main(array ^ args) +{ + bool stack_semantics = true; + + if (args->Length < 2) + { + Console::WriteLine("Usage: file_converter "); + return -1; + } + + if (stack_semantics) + { + // converter is created with stack semantics, so the destructor + // (and finalizer) get called when main exits. + FileConverter converter(args[0]); + converter.Convert(args[1]); + } + else + { + // converter used with heap semantics. Destructor is not called, + // so the file must be closed by calling the Close method. It will not + // work to close the file from the finalizer, since the StreamWriter + // object may be in an invalid state. + FileConverter^ converter = gcnew FileConverter(args[0]); + converter->Convert(args[1]); + converter->Close(); // or: delete converter; + } +} diff --git a/Chapter06/finalizer.cpp b/Chapter06/finalizer.cpp new file mode 100644 index 0000000..631928d --- /dev/null +++ b/Chapter06/finalizer.cpp @@ -0,0 +1,32 @@ +// finalizer.cpp +using namespace System; + +ref class R +{ + int ID; + + public: + + R(int id) : ID(id) { Console::WriteLine("R constructor {0}", ID); } + ~R() { Console::WriteLine("R destructor {0}", ID); } + !R() { Console::WriteLine("R finalizer {0}", ID); } +}; + +void MakeObjects() +{ + R^ r; + R r1(0); + for (int i = 1; i < 7; i++) + { + r = gcnew R(i); + } +} + +int main() +{ + MakeObjects(); + // Normally, you should avoid calling GC::Collect and forcing garbage + // collection rather than letting the garbage collection thread determine + // the best time to collect; I do it here to illustrate a point + GC::Collect(); +} diff --git a/Chapter06/finalizer_pitfalls.cpp b/Chapter06/finalizer_pitfalls.cpp new file mode 100644 index 0000000..c367c8b --- /dev/null +++ b/Chapter06/finalizer_pitfalls.cpp @@ -0,0 +1,55 @@ +// finalizer_pitfalls.cpp +#using "System.dll" +#using "System.Data.dll" + +using namespace System; +using namespace System::Data::SqlClient; + +ref class DataConnection +{ + SqlConnection^ conn; + + public: + + DataConnection() + { + conn = gcnew SqlConnection( + "Server=(local);Uid=sa;Pwd=****;Initial Catalog=master"); + conn->Open(); + } + + // ... more code ... + + ~DataConnection() + { + this->!DataConnection(); + } + + !DataConnection() + { + try { + Console::WriteLine("Closing connection..."); + conn->Close(); + } + catch(Exception^ e) + { + Console::WriteLine("Error occurred! " + e->Message); + } + } + +}; + +void UseData() +{ + DataConnection connection1; + DataConnection^ connection2 = gcnew DataConnection(); + // use the connection + +} + +int main() +{ + UseData(); + // Force a garbage collection, to illustrate a point + GC::Collect(); +} diff --git a/Chapter06/gcroot_and_auto_gcroot.cpp b/Chapter06/gcroot_and_auto_gcroot.cpp new file mode 100644 index 0000000..f67c9e2 --- /dev/null +++ b/Chapter06/gcroot_and_auto_gcroot.cpp @@ -0,0 +1,46 @@ +// gcroot_and_auto_gcroot.cpp + +#include +#include +using namespace System; +using namespace msclr; + +// managed class R +ref class R +{ + public: + void f() + { + Console::WriteLine("managed member function"); + } + + ~R() + { + Console::WriteLine("destructor"); + } + +}; + +// native class N +class N +{ + gcroot r_gcroot; + auto_gcroot r_auto_gcroot; + + public: + N() + { + r_gcroot = gcnew R(); + r_gcroot->f(); + r_auto_gcroot = gcnew R(); + r_auto_gcroot->f(); + } + +}; + +int main() +{ + N n; + // when n goes out of scope, the destructor for the auto_gcroot object + // will be executed, but not the gcroot object +} diff --git a/Chapter06/initializing_literals.cpp b/Chapter06/initializing_literals.cpp new file mode 100644 index 0000000..f430bc5 --- /dev/null +++ b/Chapter06/initializing_literals.cpp @@ -0,0 +1,25 @@ +// literal.cpp +using namespace System; + +ref class C +{ + literal String^ name = "Bob"; + + public: + + C() + { + Console::WriteLine(name); + } + + void Print() + { + Console::WriteLine(name); + } +}; + +int main() +{ + C^ c = gcnew C(); + c->Print(); +} diff --git a/Chapter06/initonly.cpp b/Chapter06/initonly.cpp new file mode 100644 index 0000000..11c38a4 --- /dev/null +++ b/Chapter06/initonly.cpp @@ -0,0 +1,26 @@ +// initonly.cpp +using namespace System; + +ref class R +{ + initonly String^ name; + + public: + + R(String^ first, String^ last) + { + name = first + last; + } + + void Print() + { + name = "Bob Jones"; // error! + Console::WriteLine(name); // OK + } +}; + +int main() +{ + R^ r = gcnew R("Mary", "Colburn"); + r->Print(); +} diff --git a/Chapter06/initonly_static.cpp b/Chapter06/initonly_static.cpp new file mode 100644 index 0000000..30059f6 --- /dev/null +++ b/Chapter06/initonly_static.cpp @@ -0,0 +1,12 @@ +// initonly_static_cpp +using namespace System; + +ref class R +{ + public: + + static initonly String^ name = "Ralph"; // OK + // initonly String^ name = "Bob"; // error! + + // rest of class declaration +}; diff --git a/Chapter06/instance_tracker.cpp b/Chapter06/instance_tracker.cpp new file mode 100644 index 0000000..05c6454 --- /dev/null +++ b/Chapter06/instance_tracker.cpp @@ -0,0 +1,63 @@ +// instance_tracker.cpp + +using namespace System; +using namespace System::Collections::Generic; + +// ref type +ref class R +{ + static List^ instanceTrackingList; + + static R() + { + instanceTrackingList = gcnew List; + } + + public: + + R(String^ s) + { + Label = s; + instanceTrackingList->Add( this ); + } + + property String^ Label; + + static int EnumerateInstances() + { + int i = 0; + for each (R^ r in instanceTrackingList) + { + i++; + Console::WriteLine( r->Label ); + } + return i; + } + + ~R() // destructor + { + // When invoking a function through the this pointer, + // use the indirection operator (->) + this->!R(); + } + + !R() // finalizer + { + instanceTrackingList->Remove( this ); + } + + // etc. +}; + +int main() +{ + R r1("ABC"); + R^ r2 = gcnew R("XYZ"); + + int count = R::EnumerateInstances(); + Console::WriteLine("Object count: " + count); + + delete r2; + count = R::EnumerateInstances(); + Console::WriteLine("Object count: " + count); +} diff --git a/Chapter06/literals_public.cpp b/Chapter06/literals_public.cpp new file mode 100644 index 0000000..7d1449f --- /dev/null +++ b/Chapter06/literals_public.cpp @@ -0,0 +1,28 @@ +// literal_public.cpp +using namespace System; + +ref class C +{ + public: + + literal String^ name = "Bob"; + + C() + { + Console::WriteLine(name); + } + + void Print() + { + Console::WriteLine(name); + } +}; + +int main() +{ + C^ c = gcnew C(); + c->Print(); + + // Access through the class: + Console::WriteLine( C::name ); +} diff --git a/Chapter06/scrabble.cpp b/Chapter06/scrabble.cpp new file mode 100644 index 0000000..943ff60 --- /dev/null +++ b/Chapter06/scrabble.cpp @@ -0,0 +1,1093 @@ +// Scrabble.cpp + +using namespace System; +using namespace System::Collections::Generic; + +enum class Characters { NEWLINE = 13 }; + +// Letter represents the different tile letters and the blank, represented +// by _ +enum class Letter { _ = 0, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, +T, U, V, W, X, Y, Z }; + +// PlayType represents the direction of play: across, down, or pass +enum class PlayType { Across, Down, Pass }; + +// The types of spaces on the board. +// DLS == Double Letter Score +// DWS == Double Word Score +// TLS == Triple Letter Score +// TWS == Triple Word Score +enum class SpaceType { Normal = 0, DLS = 1, DWS = 2, TLS = 3, TWS = 4, Center = 5 }; + +// A Scrabble Tile contains a letter and a fixed point value +// that depends on the letter. We also include a property for the +// letter that a blank tile represents once it is played. +// Tiles are not the same as board spaces: tiles are placed into +// board spaces as play goes on. +ref struct Tile +{ + property Letter LetterValue; + property int PointValue; + property Char BlankValue; + + // This array contains the static point values of each tile + // in alphabetical order, starting with the blank + static array^ point_values = + {0, 1, 3, 3, 2, 1, 4, 2, 4, 1, 8, 5, 1, 2, 1, 1, 3, 10, 1, 1, 1, 1, + 4, 3, 8, 4, 10}; + + // The Tile constructor initializes the tile from its letter + // and the point value + Tile(Letter letter) + { + LetterValue = letter; + PointValue = point_values[ safe_cast( letter )]; + } + + // Used when displaying the tile on the gameboard + virtual String^ ToString() override + { + // Format(LetterValue) won't work because the compiler + // won't be able to identify the right overload when the + // type is an enum class + return String::Format("{0}", LetterValue); + } +}; + +ref struct Player +{ + + List^ tiles; // the player's rack of tiles + + // the number of tiles in the player's rack + // normally 7, but may be less at the end of the game + property int TileCount + { + int get() { return tiles->Count; } + } + + property String^ Name; // the name of the player + + property int Score; // the player's cumulative point total + + // The constructor + Player(String^ s) + { + Name = s; + Score = 0; + Console::WriteLine("Player is {0}", Name); + } + + // Displays the player's rack of tiles + void PrintPlayerTiles() + { + Console::WriteLine("Tiles in hand: "); + for (int j = 0; j < TileCount; j++) + { + Console::Write("{0} ", tiles[j]->ToString()); + } + Console::WriteLine(); + } +}; + +// This class is the main class including all the functionality +// and data for a Scrabble game. +ref class ScrabbleGame +{ + // literals are constants that can be initialized in the class body + literal int TILE_COUNT = 100; // the number of tiles altogether + literal int MAX_TILES_IN_HAND = 7; // the maximum number of tiles in each hand + + // the array of players + array^ players; + + // spaces is the array of board spaces. + static array^ spaces = gcnew array + { { 4, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 1, 0, 0, 4 }, + { 0, 2, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 2, 0 }, + { 0, 0, 2, 0, 0, 0, 1, 0, 1, 0, 0, 0, 2, 0, 0 }, + { 1, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 1 }, + { 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0 }, + { 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0 }, + { 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0 }, + { 4, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 1, 0, 0, 4 }, + { 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0 }, + { 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0 }, + { 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0 }, + { 1, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 1 }, + { 0, 0, 2, 0, 0, 0, 1, 0, 1, 0, 0, 0, 2, 0, 0 }, + { 0, 2, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 2, 0 }, + { 4, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 1, 0, 0, 4 }}; + + // spaceTypeColors tell us how to draw the tiles when displaying the + // board at the console + static initonly array^ spaceTypeColors = { ConsoleColor::Gray, + ConsoleColor::Cyan, ConsoleColor::Red, ConsoleColor::Blue, + ConsoleColor::DarkRed, ConsoleColor::Red }; + + // the gameboard representing all played tiles + array^ gameBoard; + + // the bag, containing the tiles that have not yet been drawn + List^ bag; + + // an array of the amount of each tile + static initonly array^ tilePopulation = gcnew array + { 2, 9, 2, 2, 4, 12, 2, 3, 2, 9, 1, 1, 4, 2, 6, 8, 2, 1, 6, 4, 6, 4, 2, 2, 1, 2, + 1 }; + + int nPlayer; // the number of players in this game + int playerNum; // the current player + int moveNum; // count of the number of moves + Random^ random; // a random number generator + bool gameOver; // set to true when a condition results in the end of the game + bool endBonus; // true at the end of the game when a player uses up all of + // his or her tiles + + // pass_count counts the number of consecutive passes + // (when players do not make a play) + // This is used to find out if everyone passes one after the other, + // in which case the game is over. + int pass_count; + + // There are 15 spaces in the board. These constants are used in the static + // constructor to create the board using symmetry + literal int BOARD_SIZE = 15; + literal int BOARD_SIZEM1 = BOARD_SIZE - 1; + literal int BOARD_MID = 7; + literal int TILE_TYPES = 27; + +public: + // The instance constructor creates the array of players + // and the tile bag, which would have to be re-created for + // each game. + ScrabbleGame(unsigned int numPlayers) : nPlayer(numPlayers) + { + moveNum = 0; + random = gcnew Random(); + // Create the players + players = gcnew array(numPlayers); + for (unsigned int i = 0; i < numPlayers; i++) + { + Console::Write("Player {0} enter name: ", i); + String^ s = Console::ReadLine(); + players[i] = gcnew Player(s); + } + // Initialize the bag tiles + bag = gcnew List(TILE_COUNT); + for (int i = 0; i < TILE_TYPES; i++) + { + for (int j = 0; j < tilePopulation[i]; j++) + { + Letter letter = safe_cast(i); + bag->Add(gcnew Tile(letter)); + } + } + // The gameboard consists of an array of null pointers initially. + gameBoard = gcnew array(BOARD_SIZE, BOARD_SIZE); + } + + + // Display the current scores and tiles in the bag or + // in each player's rack + void PrintScores() + { + Console::Write("Current stats: "); + if (bag->Count != 0) + { + Console::WriteLine("{0} tiles remaining in tile bag.", bag->Count); + } + else + { + Console::WriteLine("No tiles remaining in tile bag."); + } + + for (int i = 0; i < nPlayer; i++) + { + Console::WriteLine("{0,-10} -- Score: {1,3} Number of tiles: {2} -- ", + players[i]->Name, players[i]->Score, players[i]->TileCount); + } + } + + + // Display the gameboard. This method takes a board + // as an argument, so it is possible to display the proposed + // play before committing it to the permanent gameboard. + void PrintBoard(array^ board) + { + Console::WriteLine(); + Console::Write(" "); + for (int i = 0; i < BOARD_SIZE; i++) + Console::Write(" {0:X1} ", i); + Console::WriteLine(); + for (int i = 0; i < BOARD_SIZE; i++) + { + Console::Write(" {0:X1} ", i); + for (int j = 0; j < BOARD_SIZE; j++) + { + if (board[i, j] == nullptr) + { + Console::BackgroundColor = spaceTypeColors[spaces[i, j]]; + Console::Write(" "); + // The foreground and background colors are restored to + // the colors that existed when the current process began. + Console::ResetColor(); + } + else + { + Console::BackgroundColor = ConsoleColor::Black; + Console::ForegroundColor = ConsoleColor::White; + Letter letter = board[i, j]->LetterValue; + if (letter == Letter::_) + { + Console::Write(" {0:1} ", board[i,j]->BlankValue); + } + else + { + Console::Write(" {0:1} ", board[i, j]); + } + Console::ResetColor(); + } + } + Console::WriteLine(); + } + Console::WriteLine(); + } + + // Draw a tile from the bag and return it. + // Returns null if the bag is empty. + // The parameter keep is true if the tile is drawn during the game, + // false if the tile is drawn at the beginning of the game + // to see who goes first. + Tile^ DrawTile(bool keep) + { + if (bag->Count == 0) // return nullptr if there are no tiles left + { + return nullptr; + } + int random_index = safe_cast((random->NextDouble() * bag->Count) ); + Tile^ tile = bag[random_index]; + if (keep) + bag->RemoveAt(random_index); + return tile; + } + + // Determine who goes first and draw tiles. Each player draws + // a tile and whoever has the letter closest to the beginning of + // the alphabet goes first. Return the player number of the first + // player. + int PreGame() + { + Console::WriteLine("Each player draws a tile to see who goes first.\n" + "The player closest to the beginning of the alphabet goes first."); + // Each player draws one tile to see who goes first. If both players + // draw the same tile, everyone redraws. + array^ drawTiles = gcnew array(nPlayer); + bool firstPlayerFound = false; + int firstPlayerIndex = 0; + do + { + + for (int i = 0; i < nPlayer; i++) + { + drawTiles[i] = DrawTile(false); + Console::WriteLine("{0} draws {1}.", players[i]->Name, + drawTiles[i]->LetterValue); + if (i > 0 && drawTiles[i]->LetterValue < + drawTiles[firstPlayerIndex]->LetterValue) + { + firstPlayerIndex = i; + } + } + firstPlayerFound = true; + + // if someone else has the same tile, throw back and redraw + for (int i = 0; i < nPlayer; i++) + { + if (i == firstPlayerIndex) + continue; + if (drawTiles[i]->LetterValue == + drawTiles[firstPlayerIndex]->LetterValue) + { + Console::WriteLine("Duplicate tile {0}. Redraw.", + drawTiles[i]->LetterValue); + firstPlayerFound = false; + } + } + } while (! firstPlayerFound ); + Console::WriteLine("{0} goes first.", players[firstPlayerIndex]->Name ); + + // Everyone draws their tiles + for (int i = 0; i < nPlayer; i++) + { + players[i]->tiles = gcnew List(MAX_TILES_IN_HAND); + for (int j = 0; j < MAX_TILES_IN_HAND; j++) + { + players[i]->tiles->Add( DrawTile(true)); + } + Console::Write("{0} draws tiles: ", players[i]->Name, i); + for (int j = 0; j < MAX_TILES_IN_HAND; j++) + { + Console::Write("{0} ", players[i]->tiles[j]->ToString()); + } + Console::WriteLine(); + } + return firstPlayerIndex; + } + + // Play plays the game from start to finish + // return the winning player + Player^ Play(int firstPlayer) + { + playerNum = firstPlayer; + gameOver = false; + do + { + gameOver = PlayerMove(); + playerNum = ( playerNum + 1 ) % nPlayer; + PrintScores(); + Console::WriteLine("Press ENTER to continue..."); + Console::ReadLine(); + Console::Clear(); + moveNum++; + } while (! gameOver); + + // The game is over. + AdjustPointTotals(); + Console::WriteLine("Final scores: "); + PrintScores(); + int winningPlayer = FindWinner(); + if (winningPlayer != -1) + { + return players[winningPlayer]; + } + else return nullptr; + } + + // At the end of the game, point totals are adjusted according to + // the following scheme: all players lose the point total of any + // unplayed tiles; if a player plays all her tiles, she + // receives the point totals of all unplayed tiles + void AdjustPointTotals() + { + int total_point_bonus = 0; + for (int i=0; i < nPlayer; i++) + { + if (players[i]->TileCount > 0) + { + Console::WriteLine("{0} remaining tiles and score adjustments: ", + players[i]->Name); + int point_deduction = 0; + for each (Tile^ t in players[i]->tiles) + { + Console::Write(" {0} -{1} ", t->LetterValue, t->PointValue); + point_deduction += t->PointValue; + } + Console::WriteLine(); + players[i]->Score -= point_deduction; + total_point_bonus += point_deduction; + } + } + if (endBonus) + { + Console::WriteLine("{0}'s bonus for using the last tile is {1}.", + players[playerNum]->Name, total_point_bonus); + players[playerNum]->Score += total_point_bonus; + } + } + + // Find out which player won + int FindWinner() + { + if (! gameOver) + { + return -1; + } + int leadingPlayer = 0; + for (int i = 1; i < nPlayer; i++) + { + if (players[i]->Score > players[leadingPlayer]->Score) + { + leadingPlayer = i; + } + } + for (int i = 0; i < nPlayer; i++) + { + // check for a tie + if (i != leadingPlayer && players[i]->Score == + players[leadingPlayer]->Score) + { + return -1; + } + } + return leadingPlayer; + } + + // Implement a pass move in which a player throws back a certain + // number of her tiles and draws new ones + // return true if successful + bool Pass(List^ workingTiles) + { + if (bag->Count != 0) + { + int code; + // Get the desired tiles to replace to + // the bag from the user + Console::WriteLine("Enter tiles to throw back: "); + do + { + code = Console::Read(); + wchar_t character = safe_cast(code); + Letter letter = Letter::_; + if (character == safe_cast(Characters::NEWLINE)) + { + Console::ReadLine(); + break; + } + if (character == '_') + { + letter = Letter::_; + } + else if (Char::IsLetter(character)) + { + if (Char::IsUpper(character)) + { + letter = safe_cast(character - 'A' + 1); + } + else // character is a lowercase letter + { + letter = safe_cast(character - 'a' + 1); + } + } + + // See if the letter is in the player's hand + Tile^ tile = gcnew Tile(letter); + Tile^ tileToRemove = nullptr; + bool tileFound = false; + for each (Tile^ t in workingTiles) + { + if (t->LetterValue == tile->LetterValue) + { + tileToRemove = t; + tileFound = true; + break; + } + } + if ( tileFound == true) + { + workingTiles->Remove( tileToRemove ); + bag->Add(tile); + } + else // The letter was not found. + { + Console::WriteLine("You do not have enough {0}s to pass back.", + letter); + Console::WriteLine("Press any key to continue..."); + Console::ReadLine(); + return false; + } + } while (code != safe_cast('\n')); + } // if bag->Count == 0 + + Console::Write("Are you sure you want to pass (Y/N)?"); + String^ response = Console::ReadLine(); + if (response->StartsWith( "Y") || response->StartsWith("y")) + { + if (bag->Count > 0) + { + Console::Write("{0} draws tiles: ", players[playerNum]->Name); + // Copy the working tiles to the player tiles + players[playerNum]->tiles = workingTiles; + while ( players[playerNum]->tiles->Count < MAX_TILES_IN_HAND) + { + Tile^ tile = DrawTile(true); + if (tile != nullptr) + { + players[playerNum]->tiles->Add(tile); + Console::Write(" {0} ", tile->ToString()); + } + else // the bag is empty + { + Console::WriteLine("\nThe tile bag is empty."); + break; + } + } + Console::WriteLine(); + } + } + else + { + // a false return will indicate that the user has + // changed his/her mind and may not want to pass + return false; + } + return true; + } + +private: + PlayType GetPlayType() + { + // Input the direction to play + Console::WriteLine( + "Enter Direction to Play (A = across, D = down) or P to pass:"); + String^ playTypeString = Console::ReadLine(); + + if (playTypeString == "P") + { + return PlayType::Pass; + } + if (playTypeString == "A") + { + return PlayType::Across; + } + else if (playTypeString == "D") + { + return PlayType::Down; + } + else + { + Console::WriteLine("Sorry, I didn't understand that input."); + throw gcnew Exception(); + } + } + + // Get the position of the start of the play on the board + bool GetPlayStartPosition(int% row, int% col) + { + // Input the row and column of the first letter + Console::Write( + "Enter Location to Play as [row][col]: 00 (top left) to EE (bottom right): "); + String^ locString = Console::ReadLine(); + + // parse as a hex number + int x = Int32::Parse(locString, + System::Globalization::NumberStyles::HexNumber); + row = x / 16; + col = x % 16; + if (row > 14 || col > 14 || row < 0 || col < 0) + { + Console::WriteLine("I did not understand that input."); + Console::WriteLine("The first digit is the row (0 to E);" + " the second is the column (0 to E)."); + throw gcnew Exception(); + } + + // check to see that this is an unoccupied space + if (gameBoard[row, col] != nullptr) + { + Console::WriteLine("Sorry, that space is occupied by the tile: {0}", + gameBoard[row, col]); + return false; + } + return true; + } + + // return true if the play is successful + // return false if the play is invalid and needs to be restarted + bool GetTilesForPlay(int row, int col, PlayType playType, + List^ workingTiles, array^ workingBoard ) + { + // Get the desired tiles to play from the user + Console::WriteLine( + "Enter letters to play (_ to play a blank as ): "); + int code; + + do + { + code = Console::Read(); + wchar_t character = safe_cast(code); + Letter letter = Letter::_; + if (character == safe_cast(Characters::NEWLINE)) + { + Console::ReadLine(); + break; + } + if (character == '_') + { + letter = Letter::_; + // If a blank is entered, read the next character + code = Console::Read(); + character = safe_cast(code); + } + else if (Char::IsLetter(character)) + { + if (Char::IsUpper(character)) + { + letter = safe_cast(character - 'A' + 1); + } + else // character is a lowercase letter + { + letter = safe_cast(character - 'a' + 1); + } + } + + // See if the letter is in the player's hand + Tile^ tile = gcnew Tile(letter); + if (letter == Letter::_) + { + tile->BlankValue = character; + } + Tile^ tileToRemove = nullptr; + bool tileFound = false; + for each (Tile^ t in workingTiles) + { + if (t->LetterValue == tile->LetterValue) + { + tileToRemove = t; + tileFound = true; + } + } + if ( tileFound ) + { + workingTiles->Remove( tileToRemove ); + workingBoard[row, col] = tile; + if (playType == PlayType::Across) + { + while (col < BOARD_SIZE && workingBoard[row, col] != nullptr) + { + col++; + } + // we've reached the end of the board, so the play is complete. + if (col == BOARD_SIZE) + { + // consume any additional input + Console::ReadLine(); + return true; + } + } + else + { + while (row < BOARD_SIZE && workingBoard[row, col] != nullptr) + { + row++; + } + if (row == BOARD_SIZE) + { + // consume any additional input + Console::ReadLine(); + return true; + } + } + } + else // The letter was not found. + { + Console::WriteLine("You do not have enough {0}s to play.", letter); + // consume any additional character input + Console::ReadLine(); + return false; + } + + } while (code != safe_cast('\n')); + + return true; + } + + // returns true if the player accepts the play + bool ConfirmPlay(int score) + { + Console::WriteLine("This play is worth {0} points.", score); + Console::Write("Is this your final play (Y/N)?"); + String^ response = Console::ReadLine(); + if (response->StartsWith( "Y") || response->StartsWith("y")) + { + // reset the pass count + pass_count = 0; + return true; + } + return false; + } + + // returns the number of tiles drawn + int ReplacePlayedTiles() + { + int count = 0; + Console::Write("{0} draws tiles: ", players[playerNum]->Name); + + while ( players[playerNum]->tiles->Count < MAX_TILES_IN_HAND) + { + Tile^ tile = DrawTile(true); + if (tile != nullptr) + { + count++; + players[playerNum]->tiles->Add(tile); + Console::Write(" {0} ", tile->ToString()); + } + else // the bag is empty + { + Console::WriteLine("\nThe tile bag is empty."); + return count; + } + } + Console::WriteLine(); + return count; + } + // commit the confirmed play to the permanent gameboard + void RecordPlay(List^ workingTiles, array^ workingBoard) + { + // Copy the working tiles to the player tiles + players[playerNum]->tiles = workingTiles; + + // Copy the working board to the board. + for (int i = 0; i Score += scoreForPlay; + return players[playerNum]->Score; + } + + array^ GetWorkingBoard() + { + array^ workingBoard = gcnew array(BOARD_SIZE, BOARD_SIZE); + // Copy the board into a working board + for (int i = 0; i < BOARD_SIZE; i++) + { + for (int j = 0; j < BOARD_SIZE; j++) + { + workingBoard[i, j] = gameBoard[i, j]; + } + } + return workingBoard; + } + + List^ GetWorkingTiles() + { + List^ workingTiles = gcnew List(MAX_TILES_IN_HAND); + // Copy each tile into a working hand + for each(Tile^ t in players[playerNum]->tiles) + { + workingTiles->Add(t); + } + return workingTiles; + } + +public: + + // PlayerMove implements a player making a play. + // Returns true if the game is over. + bool PlayerMove() + { + bool gameOver = false; + bool moveComplete = false; + + while (! moveComplete) + { + try + { + List^ workingTiles = GetWorkingTiles(); + array^ workingBoard = GetWorkingBoard(); + PrintBoard(gameBoard); + Console::WriteLine("{0}'s turn.", players[playerNum]->Name); + players[playerNum]->PrintPlayerTiles(); + + PlayType playType = GetPlayType(); + + if ( playType == PlayType::Pass) + { + moveComplete = Pass(workingTiles); + if (moveComplete) + { + // the pass was completed + pass_count++; + // if everyone passes and the bag is empty, the game ends + if (pass_count == nPlayer && bag->Count == 0) + { + gameOver = true; + } + return gameOver; + } + else + { + // the pass was cancelled, restart play + continue; + } + } + int row, col; + if (! GetPlayStartPosition(row, col)) + continue; + if (! GetTilesForPlay(row, col, playType, workingTiles, workingBoard)) + continue; + + // Calculate the score + int scoreForPlay = CalculateScore(row, col, playType, workingBoard); + PrintBoard(workingBoard); + if (scoreForPlay == -1) + { + Console::WriteLine("The move is not a legal move."); + if (moveNum == 0) + { + Console::WriteLine("The first play must use the center square."); + } + else + { + Console::WriteLine( + "You must use at least one existing tile on the board."); + } + Console::WriteLine(); + continue; + } + + if (!ConfirmPlay(scoreForPlay)) + continue; + + RecordPlay(workingTiles, workingBoard); + + // if more tiles are in the bag, draw tiles to replace those played + if (bag->Count > 0) + { + ReplacePlayedTiles(); + } + + // the game ends when a player "goes out" -- she uses up all + // the tiles in her hand and there are none in the bag. + // The player is eligible for the end game bonus. + if (bag->Count == 0 && players[playerNum]->tiles->Count == 0) + { + endBonus = true; + gameOver = true; + } + UpdateScore(playerNum, scoreForPlay); + moveComplete = true; + } + catch(Exception^) + { + moveComplete = false; + } + } + return gameOver; + } + + // This function calculates the score for a move, if the move is a legal play. + // If the move is not legal, returns -1. + int CalculateScore(int row, int col, PlayType direction, + array^ newBoard) + { + int cumScore = 0; + PlayType crossDirection; + + int wordScore = 0; + bool letterBonus = false; + bool wordBonus = false; + int letterMultiplier = 1; + int wordMultiplier = 1; + bool isLegalMove = false; + int tilesPlayed = 0; + + if (direction == PlayType::Down) + { + crossDirection = PlayType::Across; + // Find the start of the word being made in the main direction + while (row >= 0 && newBoard[row, col] != nullptr) + { + row--; + } + // we overshoot, so now back off by one + row++; + } + else // PlayType::Across + { + crossDirection = PlayType::Down; + while (col >= 0 && newBoard[row, col] != nullptr) + { + col--; + } + // we overshoot, so back off by one + col++; + } + + while ( row < BOARD_SIZE && col < BOARD_SIZE && newBoard[row, col] != nullptr) + { + if (moveNum == 0 && row == 7 && col == 7) + { + isLegalMove = true; + } + letterMultiplier = 1; + // if the old gameboard space here was empty, + // look at the space below the tile + if (gameBoard[row, col] == nullptr) + { + tilesPlayed++; + switch (spaces[row, col]) + { + case SpaceType::DLS: + letterBonus = true; + letterMultiplier = 2; + break; + case SpaceType::Center: + case SpaceType::DWS: + wordBonus = true; + wordMultiplier = 2; + break; + case SpaceType::TLS: + letterBonus = true; + letterMultiplier = 3; + break; + case SpaceType::TWS: + wordBonus = true; + wordMultiplier = 3; + break; + default: + break; + } + // identify any cross-words by moving backward to the + // first nonempty space + int rowCrossBegin = row; + int colCrossBegin = col; + int rowCross = row; + int colCross = col; + int crossScore = 0; + if (crossDirection == PlayType::Down) + { + while ( rowCrossBegin >= 0 && + newBoard[rowCrossBegin, colCrossBegin] != nullptr) + { + rowCrossBegin--; + } + rowCrossBegin++; // increment to beginning of word + } + else // cross-direction is across + { + while ( colCrossBegin >= 0 && + newBoard[rowCrossBegin, colCrossBegin] != nullptr) + { + colCrossBegin--; + } + colCrossBegin++; // increment to the beginning of word + } + + // Now scan forward for crosswords + int rowCrossEnd = row; + int colCrossEnd = col; + if (crossDirection == PlayType::Down) + { + while ( rowCrossEnd < BOARD_SIZE && + newBoard[rowCrossEnd, colCrossEnd] != nullptr) + { + rowCrossEnd++; + } + rowCrossEnd--; // decrement to beginning of word + } + else // cross-direction is across + { + while ( colCrossEnd < BOARD_SIZE && + newBoard[rowCrossEnd, colCrossEnd] != nullptr) + { + colCrossEnd++; + } + colCrossEnd--; // decrement to the beginning of word + } + if (rowCrossBegin != rowCrossEnd || + colCrossBegin != colCrossEnd) + { + // a crossword was found + // this counts as using existing tiles, + // so this is definitely a legal move + isLegalMove = true; + if (crossDirection == PlayType::Down) + { + for (rowCross = rowCrossBegin; rowCross <= rowCrossEnd; + rowCross++) + { + // You only account for special bonuses if the tile on that + // bonus square is one you played. + if (rowCross == row && colCross == col) + { + crossScore += newBoard[rowCross, colCross]->PointValue + * letterMultiplier; + } + else + crossScore += newBoard[rowCross, colCross]->PointValue; + } + } + else + { + for (colCross = colCrossBegin; colCross <= colCrossEnd; + colCross++) + { + if (rowCross == row && colCross == col) + { + crossScore += newBoard[rowCross, colCross]->PointValue + * letterMultiplier; + } + else + crossScore += newBoard[rowCross, colCross]->PointValue; + } + } + crossScore *= wordMultiplier; + cumScore += crossScore; + } // end of block for if there is a cross-word + + } // end of block for if the space has a new tile on it + else + { + // The space is occupied by a letter that was already there. + // All plays other than the first must contain a letter that + // is already present, so if this is the case, then the play is + // a legal play. + isLegalMove = true; + } + + wordScore += letterMultiplier * newBoard[row, col]->PointValue; + + if (direction == PlayType::Down) + row++; + else + col++; + } + wordScore *= wordMultiplier; + cumScore += wordScore; + // Fifty point bonus for using all your letters + if (tilesPlayed == MAX_TILES_IN_HAND) + { + cumScore += 50; + } + if (isLegalMove) + return cumScore; + else + return -1; + } +}; + +int main() +{ + + int nPlayer; + bool success = false; + Console::WindowHeight = 50; + do + { + Console::WriteLine( + "Welcome to Scrabble. Enter the number of players (2 to 4)."); + String^ input = Console::ReadLine(); + try + { + nPlayer = Int32::Parse(input); + if (nPlayer < 2 || nPlayer > 4) + throw gcnew Exception(); + success = true; + } + catch(Exception^ ) + { + success = false; + } + } while (! success); + + ScrabbleGame^ game = gcnew ScrabbleGame(nPlayer); + int firstPlayer = game->PreGame(); + Player^ winner = game->Play(firstPlayer); + if (winner != nullptr) + Console::WriteLine("{0} wins!", winner->Name); + Console::ReadLine(); + return 0; +} diff --git a/Chapter06/startup_code.cpp b/Chapter06/startup_code.cpp new file mode 100644 index 0000000..0e4ef29 --- /dev/null +++ b/Chapter06/startup_code.cpp @@ -0,0 +1,21 @@ +// startup_code.cpp +#include + +class Startup +{ + public: + Startup() + { + // initialize + printf("Initializing module.\n"); + } +}; +class N +{ + static Startup startup; + + N() + { + // make use of pre-initialized state + } +}; diff --git a/Chapter06/static_const_main.cpp b/Chapter06/static_const_main.cpp new file mode 100644 index 0000000..bd2b500 --- /dev/null +++ b/Chapter06/static_const_main.cpp @@ -0,0 +1,16 @@ +// static_const_main.cpp + +#using "static_const_vs_literal.dll" + +template +void f() +{ } + +int main() +{ + int a1[R::i]; // error: static const R::i isn't considered a constant + int a2[R::j]; // OK + + f(); // error + f(); // OK +} diff --git a/Chapter06/static_const_vs_literal.cpp b/Chapter06/static_const_vs_literal.cpp new file mode 100644 index 0000000..8e4eabd --- /dev/null +++ b/Chapter06/static_const_vs_literal.cpp @@ -0,0 +1,9 @@ +// static_const_vs_literal.cpp +// compile with: cl /clr /LD static_const_vs_literal.cpp + +public ref class R +{ + public: + static const int i = 15; + literal int j = 25; +}; diff --git a/Chapter06/static_constructor.cpp b/Chapter06/static_constructor.cpp new file mode 100644 index 0000000..9dbdec2 --- /dev/null +++ b/Chapter06/static_constructor.cpp @@ -0,0 +1,30 @@ +// static_constructor.cpp +using namespace System; + +ref class C +{ + private: + static String^ data; + + static C() + { + Console::WriteLine("C static constructor called."); + data = "Initialized"; + } + +public: + + C() + { + Console::WriteLine("C Constructor called."); + Console::WriteLine(data); + } + +}; + +int main() +{ + Console::WriteLine("main method"); + C c1; + C^ c2 = gcnew C(); +} diff --git a/Chapter06/valuetype_this.cpp b/Chapter06/valuetype_this.cpp new file mode 100644 index 0000000..bdad3fe --- /dev/null +++ b/Chapter06/valuetype_this.cpp @@ -0,0 +1,24 @@ +// valuetype_this.cpp + +using namespace System; + +value class V +{ + int i, j; + + public: + + void PrintStartingAddress() + { + interior_ptr ptr_to_this = this; + pin_ptr pinned_this = ptr_to_this; + Console::WriteLine("Starting address of object is 0x{0:x}", + reinterpret_cast(pinned_this)); + } +}; + +int main() +{ + V v; + v.PrintStartingAddress(); +} diff --git a/Chapter07/async_delegates.cpp b/Chapter07/async_delegates.cpp new file mode 100644 index 0000000..782451c --- /dev/null +++ b/Chapter07/async_delegates.cpp @@ -0,0 +1,82 @@ +// async_delegates.cpp + +using namespace System; +using namespace System::Threading; + +ref class R +{ +public: + property String^ Value; + + R(String^ s) { Value = s; } +}; + +delegate void QueryFunc(String^, R^); + +ref class Document +{ + + IAsyncResult^ result; + R^ m_r; + + public: + + Document(String^ s) { m_r = gcnew R(s); } + + // query the database + void Query(String^ queryString, R^ r) + { + // execute a long query + r->Value = "New Value"; + } + + void InitiateQuery(String^ queryString) + { + QueryFunc^ qf = gcnew QueryFunc(this, &Document::Query); + Console::WriteLine(m_r->Value); + result = qf->BeginInvoke(queryString, m_r, + gcnew AsyncCallback(this, &Document::ProcessResult), + qf); + } + + bool IsQueryCompleted() + { + return result->IsCompleted; + } + + // This function gets called when the asynchronous call + // completes. + void ProcessResult(IAsyncResult^ result) + { + // Retrieve the delegate. + QueryFunc^ caller = (QueryFunc^) result->AsyncState; + + // get the data back (fill in DataSet parameter) + caller->EndInvoke(result); + Console::WriteLine(m_r->Value); + } + + void UseData() + { + // do something... + } + +}; + +int main() +{ + Document doc("Old Value"); + doc.InitiateQuery("SELECT * FROM Plants WHERE Plant.Genus = 'Lycopersicon'"); + // do other work while the query executes + + // poll for completion + while (! doc.IsQueryCompleted() ) + { + Thread::Sleep(100); + } + + // do work with the data. + + doc.UseData(); + +} diff --git a/Chapter07/complex.cpp b/Chapter07/complex.cpp new file mode 100644 index 0000000..fc1e5b9 --- /dev/null +++ b/Chapter07/complex.cpp @@ -0,0 +1,3 @@ +#include "complex.h" + +int main() {} \ No newline at end of file diff --git a/Chapter07/complex.h b/Chapter07/complex.h new file mode 100644 index 0000000..93d0321 --- /dev/null +++ b/Chapter07/complex.h @@ -0,0 +1,61 @@ +// complex.h +using namespace System; + +class Complex +{ + double re; + double im; + + public: + + Complex() : re(0.0), im(0.0) { } + + Complex(double real, double imag) : re(real), im(imag) { } + + // allow a complex number to be created from a double + Complex(double real) : re(real), im(0.0) { } + + Complex(const Complex& c) + { + this->re = c.re; this->im = c.im; + } + + // assignment operator + Complex& operator=(const Complex& c) + { + this->re = c.re; this->im = c.im; + return *this; + } + + // equality operator for comparing two complex numbers + bool operator==(const Complex& c) + { + return (this->re == c.re && this->im == c.im); + } + + // unary minus + Complex operator-() + { + return Complex(-re, im); + } + + // add a complex number to a complex number + Complex operator+(const Complex& rhs) + { + return Complex(this->re + rhs.re, this->im + rhs.im); + } + // add a complex number to a complex number + Complex operator+(double d) + { + return Complex(this->re + d, this->im); + } + // add a double and a complex number + // this must be a global friend operator + friend Complex operator+(double d, Complex c) + { + return Complex(c.re + d, c.im); + } + + // ditto for ambition, distraction, uglification and derision... + +}; diff --git a/Chapter07/complex2.cpp b/Chapter07/complex2.cpp new file mode 100644 index 0000000..515e39a --- /dev/null +++ b/Chapter07/complex2.cpp @@ -0,0 +1,3 @@ +#include "complex2.h" + +int main() {} \ No newline at end of file diff --git a/Chapter07/complex2.h b/Chapter07/complex2.h new file mode 100644 index 0000000..a02903c --- /dev/null +++ b/Chapter07/complex2.h @@ -0,0 +1,40 @@ +// complex.h +using namespace System; + +value class Complex +{ + double re; + double im; + + public: + + Complex(double real, double imag) : re(real), im(imag) + { } + + // unary minus + Complex operator-() + { + return Complex(-re, im); + } + + // Addition of two complex numbers + // Could also be defined as a member operator + static Complex operator+(Complex c1, Complex c2) + { + return Complex(c1.re + c2.re, c1.im + c2.im); + } + // This cannot be a member operator, since a double is on the left + static Complex operator+(double d, Complex c) + { + return Complex(c.re + d, c.im); + } + // If Complex is the first argument, this could also be + // a member operator + static Complex operator+(Complex c, double d) + { + return Complex(c.re + d, c.im); + } + + // etc. + +}; diff --git a/Chapter07/declaring_properties.cpp b/Chapter07/declaring_properties.cpp new file mode 100644 index 0000000..39d6501 --- /dev/null +++ b/Chapter07/declaring_properties.cpp @@ -0,0 +1,23 @@ +using namespace System; + +value class ElementType +{ + public: + property unsigned int AtomicNumber; + property double AtomicWeight; + property String^ Name; + property String^ Symbol; +}; + +int main() +{ + ElementType oxygen; + oxygen.AtomicNumber = 8; + oxygen.AtomicWeight = 15.9994; + oxygen.Name = "Oxygen"; + oxygen.Symbol = "O"; + + Console::WriteLine("Element: {0} Symbol: {1}", oxygen.Name, oxygen.Symbol); + Console::WriteLine("Atomic Number: {0} Atomic Weight: {1}", + oxygen.AtomicNumber, oxygen.AtomicWeight); +} diff --git a/Chapter07/delegate_invocation_list.cpp b/Chapter07/delegate_invocation_list.cpp new file mode 100644 index 0000000..c44d6fa --- /dev/null +++ b/Chapter07/delegate_invocation_list.cpp @@ -0,0 +1,40 @@ +// delegate_invocation_list.cpp +using namespace System; + +delegate String^ MyDelegate(); + +ref class R +{ + public: + + String^ f() { return "R::f"; } + String^ g() { return "R::g"; } + String^ h() { return "R::h"; } +}; + +int main() +{ + MyDelegate^ d; + R^ r = gcnew R(); + + d = gcnew MyDelegate(r, &R::f); + // cast the return value to this particular delegate type + // note: the C-style cast evaluates to a safe_cast + d = (MyDelegate^) d->Combine(d, gcnew MyDelegate(r, &R::g)); + d = (MyDelegate^) d->Combine(d, gcnew MyDelegate(r, &R::h)); + + String^ s = d->Invoke(); + Console::WriteLine("Return value was {0}", s); + + d = (MyDelegate^) d->Remove(d, gcnew MyDelegate(r, &R::g)); + + s = d->Invoke(); + Console::WriteLine("Return value was {0}", s); + + for each (MyDelegate^ del in d->GetInvocationList()) + { + s = del->Invoke(); + Console::WriteLine("Return value was {0}", s); + } + +} diff --git a/Chapter07/delegate_operators.cpp b/Chapter07/delegate_operators.cpp new file mode 100644 index 0000000..e8aee5a --- /dev/null +++ b/Chapter07/delegate_operators.cpp @@ -0,0 +1,29 @@ +// delegate_operators.cpp + +using namespace System; + +delegate void MyDelegate(); + +ref class R +{ + public: + + void f() { Console::WriteLine("R::f"); } + void g() { Console::WriteLine("R::g"); } +}; + +int main() +{ + MyDelegate^ d; + R^ r = gcnew R(); + + d += gcnew MyDelegate(r, &R::f); + d += gcnew MyDelegate(r, &R::g); + + d->Invoke(); + + d -= gcnew MyDelegate(r, &R::g); + + // Use operator() instead of calling Invoke + d(); +} diff --git a/Chapter07/delegate_with_exceptions.cpp b/Chapter07/delegate_with_exceptions.cpp new file mode 100644 index 0000000..d740fb7 --- /dev/null +++ b/Chapter07/delegate_with_exceptions.cpp @@ -0,0 +1,37 @@ +// delegate_with_exceptions.cpp +using namespace System; + +delegate String^ MyDelegate(); + +ref class R +{ + public: + + String^ f() { throw gcnew Exception(); return "R::f"; } + String^ g() { return "R::g"; } + String^ h() { return "R::h"; } +}; + +int main() +{ + MyDelegate^ d; + R^ r = gcnew R(); + + d = gcnew MyDelegate(r, &R::f); + d = safe_cast(d->Combine(d, gcnew MyDelegate(r, &R::g))); + d = safe_cast(d->Combine(d, gcnew MyDelegate(r, &R::h))); + + for each (MyDelegate^ del in d->GetInvocationList()) + { + try + { + String^ s = del->Invoke(); + Console::WriteLine("Return value was {0}", s); + } + catch(Exception^) + { + // handle the exception + } + } + +} diff --git a/Chapter07/elementtype.cpp b/Chapter07/elementtype.cpp new file mode 100644 index 0000000..a9f6cde --- /dev/null +++ b/Chapter07/elementtype.cpp @@ -0,0 +1,14 @@ +value class ElementType +{ + public: + + property double AtomicWeight + { + double get(); + } +}; + +double ElementType::AtomicWeight::get() +{ + // same implementation as before +} diff --git a/Chapter07/eventargs.cpp b/Chapter07/eventargs.cpp new file mode 100644 index 0000000..444516d --- /dev/null +++ b/Chapter07/eventargs.cpp @@ -0,0 +1,65 @@ +// eventargs.cpp +using namespace System; + +ref class MyEventArgs : EventArgs +{ + public: + property String^ Info; + + MyEventArgs(String^ info) + { + Info = info; + } +}; + +delegate void MyEventHandler(Object^ sender, MyEventArgs^ args); + +// This class generates an event +ref class EventSender +{ + + public: + + event MyEventHandler^ MyEvent; + + void Fire(MyEventArgs^ args) + { + // raise event for starting this function + MyEvent(this, args); + + } +}; + + +// This class will handle the event +ref class EventReceiver +{ + public: + + // event handler for Start event + void OnMyEvent(Object^ sender, MyEventArgs^ args) + { + Console::WriteLine("My Event with info: " + args->Info ); + } + + void SetUpToReceive(EventSender^ sender) + { + // add the event handler + sender->MyEvent += gcnew MyEventHandler(this, &EventReceiver::OnMyEvent); + } + +}; + + +int main() +{ + EventReceiver^ receiver = gcnew EventReceiver(); + EventSender^ sender = gcnew EventSender(); + + // configure the receiver to listen to events + // from the specified sender. + receiver->SetUpToReceive(sender); + + MyEventArgs^ myargs = gcnew MyEventArgs("abc"); + sender->Fire(myargs); +} diff --git a/Chapter07/events.cpp b/Chapter07/events.cpp new file mode 100644 index 0000000..e465403 --- /dev/null +++ b/Chapter07/events.cpp @@ -0,0 +1,61 @@ +// events.cpp +using namespace System; +using namespace System::Threading; + +ref class Events +{ + public: + event EventHandler^ Start; + + event EventHandler^ Exit; + + // Function calls to raise the events from outside the class. + void RaiseStartEvent() + { + Start(this, gcnew EventArgs()); + } + + void RaiseExitEvent() + { + Exit(this, gcnew EventArgs()); + } + + // event handler for Start event + void OnStart(Object^ sender, EventArgs^ args) + { + Console::WriteLine("Starting"); + } + + // event handler for Exit event + void OnExit(Object^ sender, EventArgs^ args) + { + Console::WriteLine("Exiting"); + } +}; + +void f(Events^ e) +{ + // raise event for starting this function + e->RaiseStartEvent(); + + Console::WriteLine("Doing something."); + + // raise event for exiting this function + e->RaiseExitEvent(); +} + +int main() +{ + + Events^ events = gcnew Events(); + + // add the event handlers for Start and Exit + events->Start += gcnew EventHandler(events, &Events::OnStart); + events->Exit += gcnew EventHandler(events, &Events::OnExit); + + f(events); + + // remove the event handlers + events->Start -= gcnew EventHandler(events, &Events::OnStart); + events->Exit -= gcnew EventHandler(events, &Events::OnExit); +} diff --git a/Chapter07/events_custom.cpp b/Chapter07/events_custom.cpp new file mode 100644 index 0000000..9054db1 --- /dev/null +++ b/Chapter07/events_custom.cpp @@ -0,0 +1,114 @@ +// events_custom.cpp +#include +using namespace System; +using namespace msclr; + +ref class Events +{ + public: + + // underlying delegates to use for the events. + EventHandler^ _start, ^ _exit; + + event EventHandler^ Start + { + // Use the += operator to add a function to the + // (multicast) delegate + void add(EventHandler^ handler) + { + lock lockEvent(this); + Console::WriteLine(" Adding Start event handler. "); + _start += handler; + } + void remove(EventHandler^ handler) + { + lock lockEvent(this); + Console::WriteLine(" Removing Start event handler. "); + _start -= handler; + } + + protected: + + // If the underlying delegate is non-null, invoke the + // event with the given event arguments. + void raise(Object^ sender, EventArgs^ args) + { + Console::WriteLine(" Firing Start event. "); + if (_start) + _start->Invoke(sender, args); + } + } + + event EventHandler^ Exit + { + void add(EventHandler^ handler) + { + lock lockEvent(this); + Console::WriteLine(" Adding Exit event handler. "); + _exit += handler; + } + void remove(EventHandler^ handler) + { + lock lockEvent(this); + Console::WriteLine(" Removing Exit event handler. "); + _exit -= handler; + } + void raise(Object^ sender, EventArgs^ args) + { + Console::WriteLine(" Firing Exit event. "); + if (_exit) + _exit->Invoke(sender, args); + } + } + + // Function calls to raise the events from outside the class. + void RaiseStartEvent() + { + Start(this, gcnew EventArgs()); + } + + void RaiseExitEvent() + { + Exit(this, gcnew EventArgs()); + } + + // event handler for Start event + void OnStart(Object^ sender, EventArgs^ args) + { + Console::WriteLine("Starting"); + } + + // event handler for Exit event + void OnExit(Object^ sender, EventArgs^ args) + { + Console::WriteLine("Exiting"); + } +}; + + +void f(Events^ e) +{ + // raise event for starting this function + e->RaiseStartEvent(); + + Console::WriteLine("Doing something."); + + // raise event for exiting this function + e->RaiseExitEvent(); +} + +int main() +{ + + Events^ events = gcnew Events(); + + // add the event handlers for Start and Exit + events->Start += gcnew EventHandler(events, &Events::OnStart); + events->Exit += gcnew EventHandler(events, &Events::OnExit); + + f(events); + + // remove the event handlers + events->Start -= gcnew EventHandler(events, &Events::OnStart); + events->Exit -= gcnew EventHandler(events, &Events::OnExit); +} diff --git a/Chapter07/events_custom2.cpp b/Chapter07/events_custom2.cpp new file mode 100644 index 0000000..82c5987 --- /dev/null +++ b/Chapter07/events_custom2.cpp @@ -0,0 +1,111 @@ +// events_custom2.cpp +#include +using namespace System; +using namespace msclr; + +delegate void EventProcessor(String^ eventString); + +ref class Events +{ + public: + + // underlying delegates to use for the events. + EventProcessor^ _start, ^ _exit; + + event EventProcessor^ Start + { + void add(EventProcessor^ handler) + { + lock lockEvents(this); + Console::WriteLine(" Adding Start event handler. "); + _start += handler; + } + void remove(EventProcessor^ handler) + { + lock lockEvents(this); + Console::WriteLine(" Removing Start event handler. "); + _start -= handler; + } + + protected: + + void raise(String^ eventString) + { + Console::WriteLine(" Firing Start event. "); + if (_start) + _start->Invoke(eventString); + } + } + + event EventProcessor^ Exit + { + void add(EventProcessor^ handler) + { + lock lockEvents(this); + Console::WriteLine(" Adding Exit event handler. "); + _exit += handler; + } + void remove(EventProcessor^ handler) + { + lock lockEvents(this); + Console::WriteLine(" Removing Exit event handler. "); + _exit -= handler; + } + void raise(String^ eventString) + { + Console::WriteLine(" Firing Exit event. "); + if (_exit) + _exit->Invoke(eventString); + } + } + + // Function calls to raise the events from outside the class. + void RaiseStartEvent(String^ eventString) + { + Start(eventString); + } + + void RaiseExitEvent(String^ eventString) + { + Exit(eventString); + } + + // event handler for Start event + void OnStart(String^ eventString) + { + Console::WriteLine("Starting: " + eventString); + } + + // event handler for Exit event + void OnExit(String^ eventString) + { + Console::WriteLine("Exiting: " + eventString); + } +}; + +void f(Events^ e) +{ + // raise event for starting this function + e->RaiseStartEvent("Start event occurred!"); + + Console::WriteLine("Doing something."); + + // raise event for exiting this function + e->RaiseExitEvent("Exit event occurred."); +} + +int main() +{ + + Events^ events = gcnew Events(); + + // add the event handlers for Start and Exit + events->Start += gcnew EventProcessor(events, &Events::OnStart); + events->Exit += gcnew EventProcessor(events, &Events::OnExit); + + f(events); + + // remove the event handlers + events->Start -= gcnew EventProcessor(events, &Events::OnStart); + events->Exit -= gcnew EventProcessor(events, &Events::OnExit); +} diff --git a/Chapter07/explicit_conversion.cpp b/Chapter07/explicit_conversion.cpp new file mode 100644 index 0000000..d17da32 --- /dev/null +++ b/Chapter07/explicit_conversion.cpp @@ -0,0 +1,56 @@ +// explicit_conversion.cpp + +using namespace System; + +value class BigIntExplicit +{ + __int64 m_i; + + public: + + explicit BigIntExplicit(int i) : m_i(i) + { } + + explicit operator int() + { return m_i; } + + explicit static operator BigIntExplicit(int i) + { return BigIntExplicit(i); } + + void takeBigIntExplicit(BigIntExplicit b) {} +}; + +value class BigIntImplicit +{ + __int64 m_i; + + public: + + BigIntImplicit(int i) : m_i(i) + { } + + operator int() + { return m_i; } + + static operator BigIntImplicit(int i) + { return BigIntImplicit(i); } + + void takeBigIntImplicit(BigIntImplicit b) {} +}; + +int main() +{ + BigIntExplicit b_exp(400); + BigIntImplicit b_imp(500); + + int i = safe_cast( b_exp ); // OK: requires explicit cast + + int j = b_imp; // OK: implicit + + // int cannot implicitly be converted to BigInt1 and BigInt2 + // with the constructor; instead, you define the static conversion operator + // this is different from standard C++, which uses the constructor + // for such implicit conversions. + b_exp.takeBigIntExplicit(safe_cast(i)); + b_imp.takeBigIntImplicit(j); +} diff --git a/Chapter07/isotope_table.cpp b/Chapter07/isotope_table.cpp new file mode 100644 index 0000000..ba8a1d9 --- /dev/null +++ b/Chapter07/isotope_table.cpp @@ -0,0 +1,113 @@ +// isotope_table.cpp +using namespace System; +using namespace System::Collections::Generic; + +value class Isotope +{ + public: + property unsigned int IsotopeNumber; + property unsigned int AtomicNumber; +}; + +value class ElementType +{ + public: + property unsigned int AtomicNumber; + property double AtomicWeight; + property String^ Name; + property String^ Symbol; + + ElementType(String^ name, String^ symbol, + double a, double n) + { + AtomicNumber = n; + AtomicWeight = a; + Name = name; + Symbol = symbol; + } + + virtual String^ ToString() override + { + return String::Format( + "Element {0} Symbol {1} Atomic Number {2} Atomic Weight {3}", + Name, Symbol, AtomicNumber, AtomicWeight); + } + + property List^ Isotopes; + +}; + +ref class PeriodicTable +{ + private: + + static System::Collections::Hashtable^ table; + + public: + + static PeriodicTable() + { + table = gcnew System::Collections::Hashtable(); + + ElementType element("Hydrogen", "H", 1.0079, 1); + + // Add to the Hashtable using the key and value + table->Add(element.Name, element); + + // add the other elements... + } + + property ElementType default[String^] + { + ElementType get(String^ key) + { + return safe_cast( table[key] ); + } + } + + static property System::Collections::Hashtable^ Elements + { + System::Collections::Hashtable^ get() { return table; } + } + + }; + + + + +ref class IsotopeTable +{ + private: + + Dictionary^ isotopeTable; + + public: + + IsotopeTable() + { + isotopeTable = gcnew Dictionary(); + + // add the elements and their isotopes... + // additional code for the elements is assumed + for each (ElementType element in PeriodicTable::Elements) + { + + // Add each isotope to the isotopes table. + for each (Isotope isotope in element.Isotopes) + { + isotopeTable->Add(element.Name + isotope.IsotopeNumber, isotope); + } + } + } + + // Pass in the element symbol and isotope number, e.g "C" and 14 for + // carbon-14 + property Isotope ElementIsotope[String^, int ] + { + Isotope get(String^ key, int isotopeNumber) + { + key = key + isotopeNumber.ToString(); + return isotopeTable[key]; + } + } +}; diff --git a/Chapter07/periodic_table.cpp b/Chapter07/periodic_table.cpp new file mode 100644 index 0000000..f7dc847 --- /dev/null +++ b/Chapter07/periodic_table.cpp @@ -0,0 +1,47 @@ +// periodic_table.cpp +using namespace System; +using namespace System::Collections::Generic; + +value class Isotope +{ + public: + property double Mass; + property unsigned int AtomicNumber; +}; + +value class ElementType +{ + List^ isotopes; + List^ isotope_abundance; + double atomicWeight; + + public: + property unsigned int AtomicNumber; + property String^ Name; + property String^ Symbol; + + property double AtomicWeight + { + double get() + { + // check to see if atomic weight has been calculated yet + if (atomicWeight == 0.0) + { + if (isotopes->Count == 0) + return 0.0; + for (int i = 0; i < isotopes->Count; i++) + { + atomicWeight += isotopes[i].Mass * isotope_abundance[i]; + } + } + return atomicWeight; + } + void set(double value) + { + // used if you look up atomic weight instead of calculating it + atomicWeight = value; + } + } + + // other properties same as before +}; diff --git a/Chapter07/periodic_table2.cpp b/Chapter07/periodic_table2.cpp new file mode 100644 index 0000000..9cf5831 --- /dev/null +++ b/Chapter07/periodic_table2.cpp @@ -0,0 +1,68 @@ +// periodic_table.cpp +using namespace System; +using namespace System::Collections; + +value class ElementType +{ + public: + property unsigned int AtomicNumber; + property double AtomicWeight; + property String^ Name; + property String^ Symbol; + + // you cannot use initializer list syntax to initialize properties + ElementType(String^ name, String^ symbol, + double a, double n) + { + AtomicNumber = n; + AtomicWeight = a; + Name = name; + Symbol = symbol; + } + + // override the ToString method (you'll learn more about the override + // keyword in the next chapter) + virtual String^ ToString() override + { + return String::Format( + "Element {0} Symbol {1} Atomic Number {2} Atomic Weight {3}", + Name, Symbol, AtomicNumber, AtomicWeight); + } +}; + +ref class PeriodicTable +{ + private: + + Hashtable^ table; + + public: + + PeriodicTable() + { + table = gcnew Hashtable(); + + ElementType element("Hydrogen", "H", 1.0079, 1); + + // Add to the Hashtable using the key and value + table->Add(element.Name, element); + + // add the other elements... + } + + property ElementType default[String^] + { + ElementType get(String^ key) + { + return safe_cast( table[key] ); + } + } +}; + +int main() +{ + PeriodicTable^ table = gcnew PeriodicTable(); + + // get the element using the indexed property and print it + Console::WriteLine( table["Hydrogen"] ); +} diff --git a/Chapter07/properties_indexed1.cpp b/Chapter07/properties_indexed1.cpp new file mode 100644 index 0000000..ff6f86b --- /dev/null +++ b/Chapter07/properties_indexed1.cpp @@ -0,0 +1,35 @@ +// properties_indexed1.cpp +using namespace System; + +ref class Numbers +{ + array^ ordinals; + + public: + + Numbers() + { + ordinals = gcnew array { "zero", "one", "two", "three" }; + } + + property String^ ordinal[unsigned int] + { + String^ get(unsigned int index) + { + return ordinals[index]; + } + void set(unsigned int index, String^ value) + { + ordinals[index] = value; + } + } +}; + +int main() +{ + Numbers^ nums = gcnew Numbers(); + + // Access the property values using the indexer + // with an unsigned int as the index. + Console::WriteLine( nums->ordinal[0] ); +} diff --git a/Chapter07/properties_indexed2.cpp b/Chapter07/properties_indexed2.cpp new file mode 100644 index 0000000..bf42db3 --- /dev/null +++ b/Chapter07/properties_indexed2.cpp @@ -0,0 +1,45 @@ +// properties_indexed2.cpp +using namespace System; + +ref class Numbers +{ + array^ ordinals; + + public: + + Numbers() + { + ordinals = gcnew array { "zero", "one", "two", "three" }; + } + + property String^ default[unsigned int] + { + String^ get(unsigned int index) + { + return ordinals[index]; + } + void set(unsigned int index, String^ value) + { + ordinals[index] = value; + } + } +}; + +int main() +{ + Numbers nums; + + // Access property using array indexing operators on the + // instance directly. + Console::WriteLine( nums[0] ); + + // If using a handle, you can still use array syntax. + Numbers^ nums2 = gcnew Numbers(); + Console::WriteLine( nums2[1] ); + + // You can also use the name "default" and access like a + // named property. + + Console::WriteLine( nums.default[2] ); + Console::WriteLine( nums2->default[3] ); +} diff --git a/Chapter07/property_accessor_delegate.cpp b/Chapter07/property_accessor_delegate.cpp new file mode 100644 index 0000000..70f3335 --- /dev/null +++ b/Chapter07/property_accessor_delegate.cpp @@ -0,0 +1,20 @@ +// property_accessor_delegate.cpp +using namespace System; + +delegate double ValueAccessor(); + +value class ElementType +{ + public: + property double AtomicWeight; +}; + +int main() +{ + ElementType oxygen; + oxygen.AtomicWeight = 15.9994; + ValueAccessor^ get_method = gcnew ValueAccessor(oxygen, + &ElementType::AtomicWeight::get); + + Console::WriteLine("{0}", get_method->Invoke()); +} diff --git a/Chapter07/property_static.cpp b/Chapter07/property_static.cpp new file mode 100644 index 0000000..b8d869d --- /dev/null +++ b/Chapter07/property_static.cpp @@ -0,0 +1,14 @@ +// property_static.cpp +value class ElementType +{ + public: + + // Periodic Table of the Elements + static property array^ PeriodicTable; + + static ElementType() + { + PeriodicTable = gcnew array(120); + // initialize each element and its properties + } +}; diff --git a/Chapter07/read_only_property.cpp b/Chapter07/read_only_property.cpp new file mode 100644 index 0000000..fded1e9 --- /dev/null +++ b/Chapter07/read_only_property.cpp @@ -0,0 +1,33 @@ +ref class Atom +{ + unsigned int atomic_number; + + public: + property unsigned int IsotopeNumber; + property unsigned int AtomicNumber + { + // anyone can get the atomic number + public: unsigned int get() + { + return atomic_number; + } + + protected: void set(unsigned int n) + { + atomic_number = n; + } + } +}; + +ref class RadioactiveAtom : Atom +{ + // other stuff + + public: + + void AlphaDecay() + { + AtomicNumber -= 2; + IsotopeNumber -= 4; + } +}; diff --git a/Chapter07/sender_receiver.cpp b/Chapter07/sender_receiver.cpp new file mode 100644 index 0000000..9faa998 --- /dev/null +++ b/Chapter07/sender_receiver.cpp @@ -0,0 +1,52 @@ +// sender_receiver.cpp +using namespace System; + +// This class generates an event +ref class EventSender +{ + + public: + + event EventHandler^ MyEvent; + + void Fire(EventArgs^ args) + { + // raise event for starting this function + MyEvent(this, args); + + } +}; + + +// This class will handle the event +ref class EventReceiver +{ + public: + + // event handler for Start event + void OnMyEvent(Object^ sender, EventArgs^ args) + { + Console::WriteLine("My Event"); + } + + void SetUpToReceive(EventSender^ sender) + { + // add the event handler + sender->MyEvent += gcnew EventHandler(this, &EventReceiver::OnMyEvent); + } + +}; + + +int main() +{ + EventReceiver^ receiver = gcnew EventReceiver(); + EventSender^ sender = gcnew EventSender(); + + // configure the receiver to listen to events + // from the specified sender. + receiver->SetUpToReceive(sender); + + EventArgs^ args = gcnew EventArgs(); + sender->Fire(args); +} diff --git a/Chapter07/write_only_property.cpp b/Chapter07/write_only_property.cpp new file mode 100644 index 0000000..71e9282 --- /dev/null +++ b/Chapter07/write_only_property.cpp @@ -0,0 +1,34 @@ +ref class Atom +{ + unsigned int atomic_number; + + public: + property unsigned int IsotopeNumber; + property unsigned int AtomicNumber + { + // anyone can get the atomic number + public: unsigned int get() + { + return atomic_number; + } + // only derived classes (such as RadioactiveAtom) + // can change the atomic number + protected: void set(unsigned int n) + { + atomic_number = n; + } + } +}; + +ref class RadioactiveAtom : Atom +{ + // other stuff + + public: + + void AlphaDecay() + { + AtomicNumber -= 2; + IsotopeNumber -= 4; + } +}; diff --git a/Chapter08/abstract_classes.cpp b/Chapter08/abstract_classes.cpp new file mode 100644 index 0000000..4a0b29b --- /dev/null +++ b/Chapter08/abstract_classes.cpp @@ -0,0 +1,30 @@ +// abstract_classes.cpp + +// Notice that the abstract keyword follows the class name. +ref class GameObject abstract +{ + int ID; + + public: + + // an abstract method with no implementation + // provided + virtual void ReadFromFile() abstract; + + // a nonabstract virtual method with an implementation + virtual void UpdateID(int id) + { + ID = id; + } +}; + +ref class Monster : GameObject +{ + + public: + // overrides the base class abstract function + virtual void ReadFromFile() override + { + // code to read in data for the type + } +}; diff --git a/Chapter08/abstract_sealed.cpp b/Chapter08/abstract_sealed.cpp new file mode 100644 index 0000000..fbd5f0f --- /dev/null +++ b/Chapter08/abstract_sealed.cpp @@ -0,0 +1,18 @@ +// abstract_sealed.cpp +using namespace System; + +ref class A abstract sealed +{ + static int i = 1; + static int j = 2; + public: + static A() { Console::WriteLine("A::A()"); } + static void f() { Console::WriteLine("A::f " + i); } + static void g() { Console::WriteLine("A::g " + j); } +}; + +int main() +{ + A::f(); + A::g(); +} diff --git a/Chapter08/checking_casts.cpp b/Chapter08/checking_casts.cpp new file mode 100644 index 0000000..65fc023 --- /dev/null +++ b/Chapter08/checking_casts.cpp @@ -0,0 +1,31 @@ +// checking_casts.cpp + +using namespace System; + +ref class Base {}; +ref class Derived : Base {} ; + + +int main() +{ + +Base^ b = gcnew Base(); +Derived^ d = gcnew Derived(); +// using safe_cast +try +{ + d = safe_cast(b); +} +catch (InvalidCastException^ e) +{ + // handle error +} + +// using dynamic cast +d = dynamic_cast(b); +if (d == nullptr) +{ + // handle error +} + +} \ No newline at end of file diff --git a/Chapter08/client_game.cpp b/Chapter08/client_game.cpp new file mode 100644 index 0000000..69384fe --- /dev/null +++ b/Chapter08/client_game.cpp @@ -0,0 +1,7 @@ +// client_game.cpp +#using "game_library.dll" +ref class Scroll : Item +{ + public: + void Read() { /* read the scroll and invoke the spell */ } +}; diff --git a/Chapter08/constructor_inheritance.cpp b/Chapter08/constructor_inheritance.cpp new file mode 100644 index 0000000..e6efae4 --- /dev/null +++ b/Chapter08/constructor_inheritance.cpp @@ -0,0 +1,27 @@ +// constructor_inheritance.cpp +using namespace System; + +ref class MyBase +{ + int data; + public: + MyBase() { Console::WriteLine("MyBase::MyBase()"); } + MyBase(int data_in) : data(data_in) + { Console::WriteLine("MyBase::MyBase(int)"); } +}; + +ref class Derived : MyBase +{ + public: + // Invoke the base class constructor. + Derived(int data) : MyBase(data) + { Console::WriteLine("Derived::Derived(int)"); } + +}; + +int main() +{ + // Derived d; // illegal: ctor w/o args not inherited + MyBase b; + Derived d(100); +} diff --git a/Chapter08/constructor_order.cpp b/Chapter08/constructor_order.cpp new file mode 100644 index 0000000..b33f871 --- /dev/null +++ b/Chapter08/constructor_order.cpp @@ -0,0 +1,68 @@ +// constructor_order.cpp +using namespace System; + +class NativeClass +{ + public: + + NativeClass() + { + Console::WriteLine("NativeClass: Field constructor."); + } +}; + +ref class ManagedClass +{ + public: + + ManagedClass() + { + Console::WriteLine("ManagedClass: Field constructor."); + } +}; + +class NativeBase +{ + public: + NativeBase() + { + Console::WriteLine("NativeBase: Base class constructor."); + } +}; + +class NativeDerived : NativeBase +{ + NativeClass field; + + public: + NativeDerived() : field() + { + Console::WriteLine("Native: Derived class constructor."); + } +}; + +ref class ManagedBase +{ + public: + ManagedBase() + { + Console::WriteLine("ManagedBase: Base class constructor."); + } +}; + +ref class ManagedDerived : ManagedBase +{ + ManagedClass field; + + public: + ManagedDerived() : field() + { + Console::WriteLine("ManagedDerived: Derived class constructor."); + } +}; + +int main() +{ + NativeDerived nd; + ManagedDerived md; +} diff --git a/Chapter08/destructors_and_inheritance.cpp b/Chapter08/destructors_and_inheritance.cpp new file mode 100644 index 0000000..536f71b --- /dev/null +++ b/Chapter08/destructors_and_inheritance.cpp @@ -0,0 +1,22 @@ +// destructors_and_inheritance.cpp +using namespace System; + +ref class Base +{ + public: + Base() {} + ~Base() { Console::WriteLine("~Base"); } +}; + +ref class Derived : Base +{ + public: + Derived() { } + ~Derived() { Console::WriteLine("~Derived"); } +}; + +// The destructor will be called at the end of main. +int main() +{ + Derived d; +} diff --git a/Chapter08/explicit_override.cpp b/Chapter08/explicit_override.cpp new file mode 100644 index 0000000..0ef9b5f --- /dev/null +++ b/Chapter08/explicit_override.cpp @@ -0,0 +1,53 @@ +// explicit_override.cpp +using namespace System; + +// the game library's classes +ref class GameObject +{ + public: + void Initialize(bool fromFile) + { + if (fromFile) + { + Read(); + } + else + { + // other code + } + } + virtual void Read() + { + Console::WriteLine("GameObject::Read"); + } +}; + +ref class Item : GameObject +{ + // ... +}; + +// the user's class +ref class Scroll : Item +{ + public: + virtual void GameObjectRead() = GameObject::Read + { + // Read a file with additional parsing. + Console::WriteLine("Scroll::GameObjectRead"); + } + virtual void Read() new + { + // Read the scroll. + Console::WriteLine("Scroll::Read"); + } + +}; + +int main() +{ + Scroll^ scroll = gcnew Scroll(); + Item^ item = scroll; + item->Initialize(true); + scroll->Read(); +} diff --git a/Chapter08/finalizers_and_inheritance.cpp b/Chapter08/finalizers_and_inheritance.cpp new file mode 100644 index 0000000..1bb39a1 --- /dev/null +++ b/Chapter08/finalizers_and_inheritance.cpp @@ -0,0 +1,45 @@ +// finalizers_and_inheritance.cpp +using namespace System; + +ref class Base +{ + public: + Base() { } + ~Base() { Console::WriteLine("~Base"); this->!Base(); } + !Base() { Console::WriteLine("!Base"); } +}; + +ref class Derived : Base +{ + public: + Derived() { } + ~Derived() { Console::WriteLine("~Derived"); this->!Derived(); } + !Derived() { Console::WriteLine("!Derived"); } +}; + +void F() +{ + // Use stack semantics to create the object. + Derived d; +} + +void G() +{ + // Use the GC heap to create the object. + Derived^ dh = gcnew Derived(); + + // If you want to call the destructor for this object, + // call it explicitly here, or delete the handle. +} + +int main() +{ + // Since the destructor gets called, the finalizers + // also get called when F goes out of scope. + F(); + G(); + Console::WriteLine("Collecting after G()"); + // Force a collection of dh: finalizer only, not + // the destructor. + GC::Collect(); +} diff --git a/Chapter08/game_library.cpp b/Chapter08/game_library.cpp new file mode 100644 index 0000000..04aa1f6 --- /dev/null +++ b/Chapter08/game_library.cpp @@ -0,0 +1,16 @@ +// game_library.cpp +public ref class GameObject +{ +}; + +public ref class Monster : GameObject +{ +}; + +public ref class MapTile : GameObject +{ +}; + +public ref class Item : GameObject +{ +}; diff --git a/Chapter08/game_library2.cpp b/Chapter08/game_library2.cpp new file mode 100644 index 0000000..a5f3c8f --- /dev/null +++ b/Chapter08/game_library2.cpp @@ -0,0 +1,32 @@ +// game_library.cpp +public ref class GameObject +{ + public: + void Initialize(); + virtual void Read() { /* read data from file */ } + virtual void Write() { /* write data to file */ } +}; + +void GameObject::Initialize() +{ + if (reading_from_file) + { + Read(); // Oops! Scroll method called. + } + + // ... +} + + + +public ref class Monster : GameObject +{ +}; + +public ref class MapTile : GameObject +{ +}; + +public ref class Item : GameObject +{ +}; diff --git a/Chapter08/native_managed_virtual.cpp b/Chapter08/native_managed_virtual.cpp new file mode 100644 index 0000000..f639a66 --- /dev/null +++ b/Chapter08/native_managed_virtual.cpp @@ -0,0 +1,67 @@ +// native_managed_virtual.cpp + +class NativeBase +{ + public: + + NativeBase() + { + f_virt(); + } + + virtual void f_virt() + { + System::Console::WriteLine("NativeBase::f_virt"); + } +}; + +class NativeDerived : public NativeBase +{ + public: + + NativeDerived() + { + f_virt(); + } + + virtual void f_virt() + { + System::Console::WriteLine("NativeDerived::f_virt"); + } +}; + +ref class ManagedBase +{ + public: + + ManagedBase() + { + f_virt(); + } + + virtual void f_virt() + { + System::Console::WriteLine("ManagedBase::f_virt"); + } +}; + +ref class ManagedDerived : ManagedBase +{ + public: + + ManagedDerived() + { + f_virt(); + } + + virtual void f_virt() override + { + System::Console::WriteLine("ManagedDerived::f_virt"); + } +}; + +int main() +{ + NativeDerived nd; + ManagedDerived rd; +} diff --git a/Chapter08/new_method.cpp b/Chapter08/new_method.cpp new file mode 100644 index 0000000..a5119f7 --- /dev/null +++ b/Chapter08/new_method.cpp @@ -0,0 +1,47 @@ +// new_method.cpp +using namespace System; + +// the game library's classes +ref class GameObject +{ + public: + void Initialize(bool fromFile) + { + if (fromFile) + { + Read(); + } + else + { + // other code + } + } + virtual void Read() + { + Console::WriteLine("GameObject::Read"); + } +}; + +ref class Item : GameObject +{ + // ... +}; + +// the user's class +ref class Scroll : Item +{ + public: + virtual void Read() new + { + // Read the scroll in the game environment. + Console::WriteLine("Scroll::Read"); + } +}; + +int main() +{ + Scroll^ scroll = gcnew Scroll(); + Item^ item = scroll; + item->Initialize(true); + scroll->Read(); +} diff --git a/Chapter08/override.cpp b/Chapter08/override.cpp new file mode 100644 index 0000000..5b3cc54 --- /dev/null +++ b/Chapter08/override.cpp @@ -0,0 +1,48 @@ +// override.cpp +using namespace System; + +// the game library's classes +ref class GameObject +{ + public: + void Initialize(bool fromFile) + { + if (fromFile) + { + Read(); + } + else + { + // other code + } + } + virtual void Read() + { + // general reading from a file for the GameObject + Console::WriteLine("GameObject::Read"); + } +}; + +ref class Item : GameObject +{ + // ... +}; + +// the user's class +ref class Scroll : Item +{ + public: + virtual void Read() override + { + // special reading from a file pertaining to scroll class + Console::WriteLine("Scroll::Read"); + } +}; + +int main() +{ + Scroll^ scroll = gcnew Scroll(); + Item^ item = scroll; + item->Initialize(true); + scroll->Read(); +} diff --git a/Chapter08/sealed.cpp b/Chapter08/sealed.cpp new file mode 100644 index 0000000..8c791c3 --- /dev/null +++ b/Chapter08/sealed.cpp @@ -0,0 +1,9 @@ +// sealed.cpp + +ref class C sealed +{ +}; + +ref class B : C // Error: cannot inherit from a sealed class. +{ +}; diff --git a/Chapter08/virtual_properties.cpp b/Chapter08/virtual_properties.cpp new file mode 100644 index 0000000..19ee906 --- /dev/null +++ b/Chapter08/virtual_properties.cpp @@ -0,0 +1,23 @@ +// virtual_properties.cpp +using namespace System; + +ref class Base +{ + public: + + virtual property int Prop; +}; + +ref class Derived : Base +{ + int prop; + + public: + + virtual property int Prop + { + int get() override { return prop; } + void set(int value) override { prop = value; } + } + +}; diff --git a/Chapter08/virtual_properties2.cpp b/Chapter08/virtual_properties2.cpp new file mode 100644 index 0000000..0e29f18 --- /dev/null +++ b/Chapter08/virtual_properties2.cpp @@ -0,0 +1,109 @@ +// virtual_properties2.cpp + +using namespace System; +using namespace System::Collections::Generic; + +value class Isotope +{ + public: + property unsigned int IsotopeNumber; + property unsigned int AtomicNumber; + property double Mass; +}; + +ref class Element +{ + double atomicWeight; + + public: + property unsigned int AtomicNumber; + property String^ Name; + property String^ Symbol; + + property double AtomicWeight + { + virtual double get() { return atomicWeight; } + void set(double a) { atomicWeight = a; } + } + + Element(String^ name, String^ symbol, + double a, double n) + { + AtomicNumber = n; + AtomicWeight = a; + Name = name; + Symbol = symbol; + } +}; + +ref class HydrogenWithIsotopes : Element +{ + + + double atomicWeight; + + public: + + + property List^ Isotopes; + property List^ IsotopeAbundance; + + property double AtomicWeight + { + virtual double get() override + { + // Check to see if atomic weight has been calculated yet. + if (atomicWeight == 0.0) + { + double total = 0.0; + if (Isotopes->Count == 0) + return 0.0; + for (int i = 0; i < Isotopes->Count; i++) + { + total += Isotopes[i].Mass * IsotopeAbundance[i]; + } + atomicWeight = total /* / Isotopes->Count */ ; + } + return atomicWeight; + } + } + + + public: + + HydrogenWithIsotopes() : Element("Hydrogen", "H", 0.0, 1) + { + Isotopes = gcnew List(); + IsotopeAbundance = gcnew List(); + + Isotope isotope1; // Hydrogen 1 + isotope1.IsotopeNumber = 1; + isotope1.AtomicNumber = 1; + isotope1.Mass = 1.0078250320710; // from about.com + Isotopes->Add(isotope1); + IsotopeAbundance->Add(.99985); + + Isotope isotope2; // Hydrogen 2 : Deuterium + isotope2.IsotopeNumber = 2; + isotope2.AtomicNumber = 1; + isotope2.Mass = 2.01410177784; + Isotopes->Add(isotope2); + IsotopeAbundance->Add(.000115); + + Isotope isotope3; // Hydrogen 3 : Tritium + isotope3.IsotopeNumber = 3; + isotope3.AtomicNumber = 1; + isotope3.Mass = 3.016049277725 ; + Isotopes->Add(isotope3); + IsotopeAbundance->Add(0); // too small + + } +}; + +int main() +{ + Element e("Hydrogen", "H", 1.00794, 1); + Console::WriteLine("AtomicWeight is listed as {0}", e.AtomicWeight ); + HydrogenWithIsotopes h; + Console::WriteLine("AtomicWeight is computed as {0}", h.AtomicWeight ); +} diff --git a/Chapter09/base_and_interface.cpp b/Chapter09/base_and_interface.cpp new file mode 100644 index 0000000..4a7e892 --- /dev/null +++ b/Chapter09/base_and_interface.cpp @@ -0,0 +1,55 @@ +// base_and_interface.cpp + +using namespace System; + +ref class B +{ + public: + + virtual void f() { Console::WriteLine("B::f"); } + virtual void g() { Console::WriteLine("B::g"); } +}; + +interface class I +{ + void f(); + void g(); +}; + +ref class C : B, I +{ + public: + + // f implements I::f but doesn't override B::f + virtual void f() new + { + Console::WriteLine("C::f"); + } + // g overrides B::g AND implements I::g + virtual void g() override + { + Console::WriteLine("C::g"); + } +}; + +int main() +{ + B^ b = gcnew B(); + C^ c = gcnew C(); + I^ i = c; + + // behavior with the new specifier + b->f(); // calls B::f + c->f(); // calls C::f + i->f(); // calls C::f since C::f implements I::f + + B^ bc = c; // b pointing to instance of C + bc->f(); // calls B::f since C::f is unrelated + + // behavior with the override specifier + b->g(); // calls B::g + c->g(); // calls C::g + i->g(); // calls C::g since C::g implements I::g + + bc->g(); // calls C::g since C::g overrides B::g +} diff --git a/Chapter09/cards_enumerable.cpp b/Chapter09/cards_enumerable.cpp new file mode 100644 index 0000000..30cca8f --- /dev/null +++ b/Chapter09/cards_enumerable.cpp @@ -0,0 +1,236 @@ +// cards_enumerable.cpp + +using namespace System; +using namespace System::Text; +using namespace System::Collections; + +enum class SuitEnum { Diamonds, Clubs, Hearts, Spades }; + +// Represents a playing card +value struct Card +{ + SuitEnum Suit; + unsigned int Rank; + literal int CHAR_HEART = 3; // ASCII heart character + literal int CHAR_DIAMOND = 4; // ASCII diamond + literal int CHAR_CLUB = 5; // ASCII club + literal int CHAR_SPADE = 6; // ASCII spade + + // Render the two-character card using ASCII card values. + virtual String^ ToString() override + { + StringBuilder^ s = gcnew StringBuilder(); + if (Rank <= 0 || Rank > 13) + throw gcnew InvalidOperationException(); + else if (Rank < 11) + { + s->Append(Rank); + } + else + { + switch (Rank) + { + case 11: // Jack + s->Append("J"); + break; + case 12: // Queen + s->Append("Q"); + break; + case 13: // King + s->Append("K"); + break; + default: + throw gcnew InvalidOperationException(); + } + } + switch (Suit) + { + case SuitEnum::Clubs: + s->Append(CHAR_CLUB, 1); + break; + case SuitEnum::Hearts: + s->Append(CHAR_HEART, 1); + break; + case SuitEnum::Diamonds: + s->Append(CHAR_DIAMOND, 1); + break; + case SuitEnum::Spades: + s->Append(CHAR_SPADE, 1); + break; + default: + throw gcnew InvalidOperationException(); + } + return s->ToString(); + } +}; + + +// Cards: represents a collection of cards +ref class Cards : IEnumerable +{ + array^ card_array; + + literal int K = 13; // King's ordinal position + literal int CARDS_IN_DECK = 52; // Cards in the deck +public: + Cards() + { + // Create a standard deck. + card_array = gcnew array(CARDS_IN_DECK + 1); + for (int i = 1; i <= K; i++) + { + card_array[i].Suit = SuitEnum::Diamonds; + card_array[i].Rank = i; + card_array[i + K].Suit = SuitEnum::Clubs; + card_array[i + K].Rank = i; + card_array[i + 2*K].Suit = SuitEnum::Hearts; + card_array[i + 2*K].Rank = i; + card_array[i + 3*K].Suit = SuitEnum::Spades; + card_array[i + 3*K].Rank = i; + } + } + + Cards(const Cards% c) + { + card_array = gcnew array(c.card_array->Length); + for (int i = 0; i < c.card_array->Length; i++) + { + card_array[i] = c.card_array[i]; + } + } + + // Default indexed property. Allows use of + // Cards[i] syntax to get a card by index. + property Card default[int] + { + Card get(int index) + { + return card_array[index]; + } + void set(int index, Card card) + { + card_array[index] = card; + } + } + + // The number of cards in this collection + property int Count + { + int get() + { + return card_array->Length; + } + } + + // Shuffle the cards in this collection. + void Shuffle() + { + // Swap 5000 cards. + Random^ random = gcnew Random(); + for (int i = 0; i < 5000; i++) + { + int card1 = (int)Math::Ceiling(random->NextDouble() * CARDS_IN_DECK); + int card2 = (int)Math::Ceiling(random->NextDouble() * CARDS_IN_DECK); + Card temp = this[card1]; + this[card1] = this[card2]; + this[card2] = temp; + } + } + +private: + // IEnumerable::GetEnumerator method + // Compiler requires a private virtual method to be marked sealed. + virtual System::Collections::IEnumerator^ GetEnumeratorNonGeneric() sealed + = System::Collections::IEnumerable::GetEnumerator + { + return GetEnumerator(); + } + +public: + // Nonvirtual GetEnumerator method for efficiency; the virtual + // methods call the nonvirtual method. For each uses the + // nonvirtual method. + IEnumerator^ GetEnumerator() + { + return (IEnumerator^) gcnew CardEnumerator(this); + } + + // nested enumerator class + ref class CardEnumerator : IEnumerator + { + int current; + Cards^ cards; + + public: + CardEnumerator(Cards^ cards_in) + { + // Snapshot the collection by calling the copy constructor. + cards = gcnew Cards(*cards_in); + // The enumerator should always start *before* the first element, so + // in a zero-based collection that is -1, but here it is 0. + current = 0; + } + + private: + // implements the IEnumerator Current property + virtual property Object^ _Current + { + // Use explicit interface implementation syntax on the get + // method, not the property. The compiler requires a private + // virtual method to be marked "sealed". + Object^ get() sealed = System::Collections::IEnumerator::Current::get + { + return Current; + } + } + + public: + + // nonvirtual Current property for maximum efficiency + property Card Current + { + Card get() + { + if (current <= 0 || current >= cards->Count ) + throw gcnew InvalidOperationException(); + return cards[current]; + } + } + + // Implements the IEnumerator::MoveNext method + virtual bool MoveNext() + { + current++; + if (current <= 0 || current > cards->Count) + throw gcnew InvalidOperationException(); + else + return current < cards->Count; + } + + // implements the IEnumerator::Reset method + virtual void Reset() + { + current = 0; + } + }; + +}; + +void PrintAll(Cards^ deck) +{ + for each (Card c in deck) + { + Console::Write("{0} ", c, c.Rank, c.Suit); + // has no effect on iteration since collection is snapshot + // but deck will remain shuffled when next used + deck->Shuffle(); + } + Console::WriteLine(); +} + +int main() +{ + Cards^ deck = gcnew Cards(); + PrintAll(deck); + PrintAll(deck); +} diff --git a/Chapter09/class_interface_method_ambiguity.cpp b/Chapter09/class_interface_method_ambiguity.cpp new file mode 100644 index 0000000..8f11cf5 --- /dev/null +++ b/Chapter09/class_interface_method_ambiguity.cpp @@ -0,0 +1,31 @@ +// class_interface_method_ambiguity.cpp +using namespace System; + +interface class IA +{ + void f(); +}; + +ref class A : IA +{ + public: + + // Note that new is not used here. + void f() + { + Console::WriteLine("A::f"); + } + // Explicit implementation syntax + virtual void fIA() = IA::f + { + Console::WriteLine("A::fIA implementing IA::f"); + } +}; + +int main() +{ + A^ a = gcnew A(); + IA^ ia = a; + ia->f(); + a->f(); +} diff --git a/Chapter09/explicit_interface_implementation.cpp b/Chapter09/explicit_interface_implementation.cpp new file mode 100644 index 0000000..f2b4eac --- /dev/null +++ b/Chapter09/explicit_interface_implementation.cpp @@ -0,0 +1,41 @@ +// explicit_interface_implementation.cpp +using namespace System; + +interface class I1 { void f(); }; + +interface class I2 { void f(); }; + +ref class R : I1, I2 +{ + public: + + virtual void f1() = I1::f + { + Console::WriteLine("R::f1 == I1::f"); + } + + virtual void f2() = I2::f + { + Console::WriteLine("R::f2 == I2::f"); + } +}; + +int main() +{ + + R^ r = gcnew R(); + + I1^ i1 = r; + I2^ i2 = r; + + r->f1(); // OK -- call through the object. + r->f2(); // OK -- call through the object. + + // r->f(); // Error: f is not a member of R. + + i1->f(); // OK -- call f1. + i2->f(); // OK -- call f2. + + // r->I1::f(); // Compiler error: "direct call will fail at runtime". + // r->I1::f1(); // Error: f1 is not a member of I1. +} diff --git a/Chapter09/extension_monster.cpp b/Chapter09/extension_monster.cpp new file mode 100644 index 0000000..4d0fecf --- /dev/null +++ b/Chapter09/extension_monster.cpp @@ -0,0 +1,33 @@ +using namespace System; + +namespace MonsterExtensions +{ + +public ref class Attack +{ + +}; + +public enum class AttackTypeEnum +{ + Type1, + Type2 +}; + +public ref class AnimationSequence2D +{ + +}; + +public interface class IMonster +{ + + property String^ Name; + property int Strength; + property AnimationSequence2D^ Frames; + Attack^ GenerateAttack(AttackTypeEnum attacktype); + void DefendAttack(Attack^ attack); + // etc. +}; + +} \ No newline at end of file diff --git a/Chapter09/interface.cpp b/Chapter09/interface.cpp new file mode 100644 index 0000000..e929a81 --- /dev/null +++ b/Chapter09/interface.cpp @@ -0,0 +1,14 @@ +interface class IInterface +{ + void f(); + int g(); +}; + +ref class R : IInterface +{ + public: + // The virtual keyword is required to implement the interface method. + virtual void f() { } + + virtual int g() { return 1; } +}; diff --git a/Chapter09/interface_list.cpp b/Chapter09/interface_list.cpp new file mode 100644 index 0000000..2a08f7e --- /dev/null +++ b/Chapter09/interface_list.cpp @@ -0,0 +1,21 @@ +// interface_list.cpp + +using namespace System; + +interface class IA {}; +interface class IB {}; + +ref class Base : IA // OK +{ }; + +ref class Derived : Base, IA // OK : Base class first +{ }; + +ref class A : Object, IA // OK: Object may be explicitly stated. +{ }; + +value class V : ValueType, IA // OK: Value class inherits from ValueType. +{ }; + +ref class B : IB, Base // OK. Base class need not appear first (as in C#). +{ }; diff --git a/Chapter09/interface_name_collision.cpp b/Chapter09/interface_name_collision.cpp new file mode 100644 index 0000000..aec57e9 --- /dev/null +++ b/Chapter09/interface_name_collision.cpp @@ -0,0 +1,20 @@ +// interface_name_collision.cpp +using namespace System; + +interface class I1 { void f(); }; + +interface class I2 { void f(); }; + +ref class R : I1, I2 +{ + public: + + virtual void f() + { Console::WriteLine("R::f"); } +}; + +int main() +{ + R^ r = gcnew R(); + r->f(); // R::f() implements both I1's f and I2's f +} diff --git a/Chapter09/interface_private.cpp b/Chapter09/interface_private.cpp new file mode 100644 index 0000000..cad17fa --- /dev/null +++ b/Chapter09/interface_private.cpp @@ -0,0 +1,28 @@ +// interface_private.cpp + +interface class IInterface +{ + + void f(); + int g(); +}; + +ref class R : IInterface +{ + // The virtual keyword is required to implement the interface. + virtual void f() sealed = IInterface::f + { } + + public: + virtual int g() { return 1; } +}; + +int main() +{ + R^ r = gcnew R(); + IInterface^ ir = r; + ir->f(); // f may be called through the interface. + + // r->f(); // Error: f is private. + r->g(); // OK +} diff --git a/Chapter09/interface_properties_events.cpp b/Chapter09/interface_properties_events.cpp new file mode 100644 index 0000000..c10b584 --- /dev/null +++ b/Chapter09/interface_properties_events.cpp @@ -0,0 +1,31 @@ +// interface_properties_events.cpp +using namespace System; + +interface class I +{ + property int P1; + event EventHandler^ E; + + property int P2 + { + int get(); + void set(int v); + } +}; + +ref class R : I +{ + int value; + + public: + + virtual property int P1; + virtual event EventHandler^ E; + + virtual property int P2 + { + int get() { return value; } + void set(int v) { value = v; } + } + +}; diff --git a/Chapter09/interfaces_constants.cpp b/Chapter09/interfaces_constants.cpp new file mode 100644 index 0000000..8ac4c12 --- /dev/null +++ b/Chapter09/interfaces_constants.cpp @@ -0,0 +1,8 @@ +// interfaces_constants.cpp + +interface class I +{ + static const int i = 100; // OK : static members OK + literal int j = 50; // OK : literals OK + // const int k; // error : nonstatic field +}; diff --git a/Chapter09/interfaces_implementing_interfaces.cpp b/Chapter09/interfaces_implementing_interfaces.cpp new file mode 100644 index 0000000..ae69b2f --- /dev/null +++ b/Chapter09/interfaces_implementing_interfaces.cpp @@ -0,0 +1,12 @@ +// interfaces_implementing_interfaces.cpp + +interface class IA { void f(); }; + +interface class IB : IA { void g(); }; + +ref class R : IB +{ + public: + virtual void f() {} + virtual void g() {} +}; diff --git a/Chapter09/interfaces_multiple.cpp b/Chapter09/interfaces_multiple.cpp new file mode 100644 index 0000000..3a6b8f9 --- /dev/null +++ b/Chapter09/interfaces_multiple.cpp @@ -0,0 +1,13 @@ +// interfaces_multiple.cpp + +interface class IA { void f(); }; + +interface class IB { void g(); }; + +// Implement multiple interfaces: +ref class R : IA, IB +{ + public: + virtual void f() {} + virtual void g() {} +}; diff --git a/Chapter09/interfaces_static.cpp b/Chapter09/interfaces_static.cpp new file mode 100644 index 0000000..abf1a9e --- /dev/null +++ b/Chapter09/interfaces_static.cpp @@ -0,0 +1,28 @@ +// interfaces_static.cpp +using namespace System; + +interface class IA +{ + static int i = 6; + static const int j = 100; + + static void f() { Console::WriteLine("IA::f " + i); } +}; + +ref class A : IA +{ + public: + + static void f() { Console::WriteLine("A::f " + IA::j); } +}; + +int main() +{ + A^ a = gcnew A(); + IA^ ia = a; + ia->f(); // Call IA::f through interface handle. + a->f(); // Call A::f through object handle. + IA::f(); // Call IA::f. + A::f(); // Call A::f. + a->IA::f(); // Call IA::f +} diff --git a/Chapter09/message_comparable_generic.cpp b/Chapter09/message_comparable_generic.cpp new file mode 100644 index 0000000..c2a507c --- /dev/null +++ b/Chapter09/message_comparable_generic.cpp @@ -0,0 +1,84 @@ +// message_comparable_generic.cpp + +using namespace System; + +enum class SortByEnum +{ + SortByDate, + SortByFrom, + SortBySubject +}; + +ref class Message : IComparable +{ + public: + + static property SortByEnum SortCriterion; + + property DateTime DateReceived; + property String^ From; + property String^ Subject; + property String^ Body; + + Message(DateTime dt, String^ from, String^ subject, String^ body) + { + DateReceived = dt; + From = from; + Subject = subject; + Body = body; + } + + virtual int CompareTo(Message^ msg) + { + + switch ( SortCriterion ) + { + case SortByEnum::SortByDate: + return this->DateReceived.CompareTo(msg->DateReceived); + case SortByEnum::SortByFrom: + return this->From->CompareTo(msg->From); + case SortByEnum::SortBySubject: + return this->Subject->CompareTo(msg->Subject); + default: + throw gcnew InvalidOperationException(); + } + + } + + // other methods... + +}; + +// Print the message headers in sorted order. +void PrintHeaders(array^ messages, SortByEnum sortOrder) +{ + Message::SortCriterion = sortOrder; + Array::Sort(messages); + + for (int i = 0; i < messages->Length; i++) + { + Console::WriteLine("Received: {0} From: <{1}> Subject: {2}", + messages[i]->DateReceived, messages[i]->From, + messages[i]->Subject ); + } + Console::WriteLine(); +} + + +int main() +{ + // Create some messages. + array^ message_array = + { + gcnew Message( DateTime(2006, 1, 12), "Nancy Carlisle", "Dog Jokes", ""), + gcnew Message( DateTime(2006, 1, 15), "George Jones", "Bark mulch order", ""), + gcnew Message( DateTime(2006, 1, 2), "George Jones", "Bark mulch offer", ""), + gcnew Message( DateTime(2005, 12, 31), "Jeni Hogenson", + "Wedding Anniversary", "") + }; + + PrintHeaders(message_array, SortByEnum::SortByDate); + PrintHeaders(message_array, SortByEnum::SortByFrom); + PrintHeaders(message_array, SortByEnum::SortBySubject); + +} diff --git a/Chapter09/mymonster.cpp b/Chapter09/mymonster.cpp new file mode 100644 index 0000000..47ffd92 --- /dev/null +++ b/Chapter09/mymonster.cpp @@ -0,0 +1,23 @@ + +#using "extension_monster.dll" + +using namespace System; +using namespace MonsterExtensions; + +public ref class MyMonster : IMonster +{ + public: + + virtual property String^ Name; + virtual property int Strength; + virtual property AnimationSequence2D^ Frames; + + + virtual Attack^ GenerateAttack(AttackTypeEnum attacktype) + { + return gcnew Attack(); + } + virtual void DefendAttack(Attack^ attack) + { + } +}; \ No newline at end of file diff --git a/Chapter09/usemonster.cpp b/Chapter09/usemonster.cpp new file mode 100644 index 0000000..a9f11ff --- /dev/null +++ b/Chapter09/usemonster.cpp @@ -0,0 +1,21 @@ +#using "extension_monster.dll" + +using namespace System; +using namespace MonsterExtensions; +using namespace System::Reflection; + +IMonster^ GetExtensionMonsterInterface(String^ userMonsterAssemblyFileName, + String^ userMonsterClassName) +{ + Assembly^ userMonsterAssembly = Assembly::LoadFrom( userMonsterAssemblyFileName ); + IMonster^ userMonster = (IMonster^) userMonsterAssembly->CreateInstance( + userMonsterClassName ); + return userMonster; +} + +int main() +{ + IMonster^ monster = GetExtensionMonsterInterface("mymonster.dll", "MyMonster"); + + Attack^ attack = monster->GenerateAttack(AttackTypeEnum::Type1); +}; \ No newline at end of file diff --git a/Chapter10/appdomain_execute_assembly.cpp b/Chapter10/appdomain_execute_assembly.cpp new file mode 100644 index 0000000..492020d --- /dev/null +++ b/Chapter10/appdomain_execute_assembly.cpp @@ -0,0 +1,24 @@ +// appdomain_execute_assembly.cpp + +using namespace System; + +int main() +{ + try + { + String^ exeName; + AppDomain^ appDomain = AppDomain::CreateDomain("Reflection2"); + Console::Write("Enter a pure or safe executable to execute:"); + exeName = Console::ReadLine(); + Console::WriteLine("Executing assembly {0}", exeName); + + // You must compile the example program with the /clr:pure or /clr:safe option + appDomain->ExecuteAssembly(exeName); + } + catch(Exception^ e) + { + Console::WriteLine("{0} {1}", e->Message, e->StackTrace); + } + + Console::WriteLine("Finished."); +} \ No newline at end of file diff --git a/Chapter10/consume_outattr.cs b/Chapter10/consume_outattr.cs new file mode 100644 index 0000000..0588a27 --- /dev/null +++ b/Chapter10/consume_outattr.cs @@ -0,0 +1,17 @@ +// consume_outattr.cs +// compile with: /r:outattribute.dll + +using System; +using OutAttrClass; + +public class C +{ + + public static void Main() + { + C1 c1 = new C1(); + String str = "old value"; + c1.Func(out str); + Console.WriteLine(str); + } +}; diff --git a/Chapter10/custom_attribute.cpp b/Chapter10/custom_attribute.cpp new file mode 100644 index 0000000..8070f66 --- /dev/null +++ b/Chapter10/custom_attribute.cpp @@ -0,0 +1,42 @@ +// custom_attribute.cpp + +using namespace System; + +// This attribute is applied to custom attributes and indicates the targets for +// the attribute, among other things. In this case, we accept the defaults. +[AttributeUsageAttribute(AttributeTargets::All)] +public ref class OwnerAttribute : Attribute +{ + public: + property String^ DevOwner; + property String^ TestOwner; + property String^ PMOwner; + property String^ DocOwner; + property DateTime^ CreationDate; + + OwnerAttribute() + { } + + OwnerAttribute(String^ _DevOwner, String^ _TestOwner, + String^ _PMOwner, String^ _DocOwner) + { + DevOwner = _DevOwner; + TestOwner = _TestOwner; + PMOwner = _PMOwner; + DocOwner = _DocOwner; + } +}; + +// Parameter order +[ Owner("John Smith", "Jane Doe", "Hubert Eliason", "Edgar Odell")] +ref class C1 +{ + // etc. +}; + +// Using named parameters +[ Owner(DevOwner="John Smith") ] +ref class C2 +{ + // etc. +}; diff --git a/Chapter10/custom_attributes2.cpp b/Chapter10/custom_attributes2.cpp new file mode 100644 index 0000000..efd893f --- /dev/null +++ b/Chapter10/custom_attributes2.cpp @@ -0,0 +1,35 @@ +// custom_attribute2.cpp + +using namespace System; + +// Specify what targets this attribute may be applied to using the +// AttributeUsageAttribute. +[AttributeUsageAttribute(AttributeTargets::Assembly | AttributeTargets::Class)] +public ref class OwnerAttribute : Attribute +{ + public: + // Public properties can be used as named parameters. + property String^ DevOwner; + property String^ TestOwner; + + OwnerAttribute() { } + +}; + +// Using named parameters +[ Owner(DevOwner="John Smith") ] +ref class C2 +{ + // etc. +}; + + +int main() +{ + Attribute^ attribute = Attribute::GetCustomAttribute( C2::typeid, + OwnerAttribute::typeid); + if (attribute != nullptr) + { + Console::WriteLine("{0}", attribute); + } +} diff --git a/Chapter10/exception.cpp b/Chapter10/exception.cpp new file mode 100644 index 0000000..e3bc960 --- /dev/null +++ b/Chapter10/exception.cpp @@ -0,0 +1,21 @@ +// exception.cpp +using namespace System; + +int main() +{ + +try +{ + bool error = true; + // other code + + if (error) + { + throw gcnew Exception(); + } +} +catch( Exception^ exception) +{ + // code to handle the exception +} +} diff --git a/Chapter10/exception_properties.cpp b/Chapter10/exception_properties.cpp new file mode 100644 index 0000000..2532731 --- /dev/null +++ b/Chapter10/exception_properties.cpp @@ -0,0 +1,25 @@ +// exception_properties.cpp + +using namespace System; + +int main() +{ + + try + { + bool error = true; + // other code + + if (error) + { + throw gcnew Exception("XYZ"); + } + } + catch( Exception^ exception) + { + Console::WriteLine("Exception Source property {0}", exception->Source); + Console::WriteLine("Exception StackTrace property {0}", + exception->StackTrace); + Console::WriteLine("Exception Message property {0}", exception->Message); + } +} diff --git a/Chapter10/exceptions_ctor.cpp b/Chapter10/exceptions_ctor.cpp new file mode 100644 index 0000000..3828397 --- /dev/null +++ b/Chapter10/exceptions_ctor.cpp @@ -0,0 +1,61 @@ +// exceptions_ctor.cpp + +using namespace System; + +// the type of the member +ref class Class1 +{ + public: + + Class1() + { + // Assume a fatal problem has occurred here. + throw gcnew Exception(); + } +}; + +ref class A +{ + + Class1^ c1; + Class1^ c2; + + public: + + A() + { + // c1 has a problem; it throws an exception. + c1 = gcnew Class1(); + + // c2 never gets created. + c2 = gcnew Class1(); + } + + void F() { Console::WriteLine("Testing F"); } + + ~A() // Never gets called, even if A is created with stack semantics + { + Console::WriteLine("A::~A()"); + } + + !A() // Gets called for partially constructed object + { + Console::WriteLine("A::!A()"); + // Don't try to use C2 here without checking for null first. + } + +}; + +int main() +{ + //try + //{ + A a; + a.F(); // never reached + //} + //catch( Exception^ e) + //{ + // Console::WriteLine("In catch block."); + //} + +} diff --git a/Chapter10/exceptions_custom.cpp b/Chapter10/exceptions_custom.cpp new file mode 100644 index 0000000..ea129eb --- /dev/null +++ b/Chapter10/exceptions_custom.cpp @@ -0,0 +1,39 @@ +// exceptions_custom.cpp +using namespace System; + +ref class MyException : Exception +{ + public: + + virtual property String^ Message + { + String^ get() override + { + return "You must supply a command-line argument."; + } + } +}; + +int main(array^ args) +{ + try + { + if (args->Length < 1) + { + throw gcnew MyException(); + } + throw gcnew Exception(); + } + // The first catch blocks are the specific exceptions that + // you are looking for. + catch (MyException^ e) + { + Console::WriteLine("MyException occurred! " + e->Message); + } + // You may also catch other exceptions with multiple try blocks, + // although it's better. + catch (Exception^ exception) + { + Console::WriteLine("Unknown exception!"); + } +} diff --git a/Chapter10/finally_block.cpp b/Chapter10/finally_block.cpp new file mode 100644 index 0000000..81b238d --- /dev/null +++ b/Chapter10/finally_block.cpp @@ -0,0 +1,16 @@ +using namespace System; + +int main() { + +try + { + // ... + } + catch( Exception^ ) + { + } + finally + { + Console::WriteLine("finally block!"); + } +} \ No newline at end of file diff --git a/Chapter10/multiple_finally_blocks.cpp b/Chapter10/multiple_finally_blocks.cpp new file mode 100644 index 0000000..3485987 --- /dev/null +++ b/Chapter10/multiple_finally_blocks.cpp @@ -0,0 +1,34 @@ +// multiple_finally_blocks.cpp + +using namespace System; + +int main() +{ + try + { + Console::WriteLine("Outer try"); + + try + { + Console::WriteLine("Inner try"); + throw gcnew Exception("XYZ"); + + } + catch( Exception^ exception) + { + Console::WriteLine("Inner catch"); + } + finally + { + Console::WriteLine("Inner finally"); + } + } + catch(Exception^ exception) + { + Console::WriteLine("Outer catch"); + } + finally + { + Console::WriteLine("Outer finally"); + } +} diff --git a/Chapter10/obsolete.cpp b/Chapter10/obsolete.cpp new file mode 100644 index 0000000..8697a7e --- /dev/null +++ b/Chapter10/obsolete.cpp @@ -0,0 +1,17 @@ +// obsolete.cpp +using namespace System; + +ref class C +{ + public: + void Method2() {} + [Obsolete("This method is obsolete; use Method2 instead.")] + void Method1() {} +}; + +int main() +{ + C^ c = gcnew C(); + c->Method1(); + c->Method2(); +} diff --git a/Chapter10/outattribute.cpp b/Chapter10/outattribute.cpp new file mode 100644 index 0000000..44a9825 --- /dev/null +++ b/Chapter10/outattribute.cpp @@ -0,0 +1,19 @@ +// outattribute.cpp +// compile with: /clr:safe or /clr:pure + +using namespace System; +using namespace System::Runtime::InteropServices; + +namespace OutAttrClass +{ + + public ref class C1 + { + public: + + void Func([Out] String^% text) + { + text = "testing"; + } + }; +} diff --git a/Chapter10/plants.xml b/Chapter10/plants.xml new file mode 100644 index 0000000..111cad4 --- /dev/null +++ b/Chapter10/plants.xml @@ -0,0 +1,8 @@ + + + 4 + Medium + PartShade + brevipedunculata + Ampelopsis + \ No newline at end of file diff --git a/Chapter10/reflection2.cpp b/Chapter10/reflection2.cpp new file mode 100644 index 0000000..85f7103 --- /dev/null +++ b/Chapter10/reflection2.cpp @@ -0,0 +1,48 @@ +// reflection2.cpp + +using namespace System; +using namespace System::Reflection; + +// A class to reflect upon + +ref class Reflector +{ + public: + + void TestDynamicCall(String^ greeting) + { + Console::Beep(); + Console::WriteLine(greeting); + Console::WriteLine("Dynamic Call succeeded!"); + } + + // Load an assembly and invoke the specified method on the specified type. + void LoadAndReflect(String^ assemblyFileName, String^ typeName, + String^ methodName, array^ parameterList) + { + // Load the assembly. + Assembly^ assembly = Assembly::LoadFrom(assemblyFileName); + + // Get the type. + Type^ t= assembly->GetType(typeName); + + // Get the method of interest. + MethodInfo^ method = t->GetMethod(methodName); + + // Create an instance of the object. + Object^ obj = Activator::CreateInstance(t); + + // Invoke the method. + method->Invoke(obj, parameterList); + + } +}; + +int main() +{ + Reflector r ; + // Pass the assembly file name, class name, and method name, and the + // parameter list. + array^ params = gcnew array { "Hello!" }; + r.LoadAndReflect("reflection2.exe", "Reflector", "TestDynamicCall", params); +} diff --git a/Chapter10/reflection_general.cpp b/Chapter10/reflection_general.cpp new file mode 100644 index 0000000..14fb07f --- /dev/null +++ b/Chapter10/reflection_general.cpp @@ -0,0 +1,56 @@ +// reflection_general.cpp + +using namespace System; +using namespace System::Reflection; + +// A class to reflect upon + +ref class Reflector +{ + public: + + // Load an assembly, and print out the methods in the types in the + // assembly, invoking the specified method on the specified type. + void LoadAndReflect(String^ assemblyFileName) + { + Assembly^ assembly = Assembly::LoadFrom(assemblyFileName); + array^ types = assembly->GetTypes(); + for each (Type^ t in types) + { + Console::WriteLine(t->ToString()); + + // Get the methods and loop over them. + array^ methods = t->GetMethods(); + for each (MethodInfo^ method in methods) + { + Console::Write(" {0} {1}(", method->ReturnType->ToString(), + method->Name); + // Get the parameters and loop over them. + array^ params = method->GetParameters(); + // We don't use for each here because we need to use the index + // to determine whether a comma is needed. + for (int i = 0; i < params->Length; i++) + { + ParameterInfo^ param = params[i]; + Console::Write("{0} {1}", + param->ParameterType->ToString(), + param->Name); + if (i < params->Length - 1) + Console::Write(", "); + } + Console::WriteLine(")"); + } + } + } +}; + +int main(array^ args) +{ + Reflector^ r = gcnew Reflector(); + // Pass the assembly file name and reflect over it. + for each (String^ s in args) + { + Console::WriteLine("Reflection on {0}", s); + r->LoadAndReflect(s); + } +} diff --git a/Chapter10/return_value_attribute.cpp b/Chapter10/return_value_attribute.cpp new file mode 100644 index 0000000..3d1d637 --- /dev/null +++ b/Chapter10/return_value_attribute.cpp @@ -0,0 +1,7 @@ + +ref class ReturnType {}; + +ref class ReturnValueAttribute : System::Attribute {}; + +[ returnvalue: ReturnValueAttribute() ] +ReturnType^ GetValue(); diff --git a/Chapter10/runtimewrappedexception.cs b/Chapter10/runtimewrappedexception.cs new file mode 100644 index 0000000..51bdd69 --- /dev/null +++ b/Chapter10/runtimewrappedexception.cs @@ -0,0 +1,22 @@ +// runtimewrappedexception.cs +using System; +using System.Collections.Generic; +using System.Text; +using System.Runtime.CompilerServices; + +class Program +{ + static void Main(string[] args) + { + try + { + R.TrySomething(); + } + catch (RuntimeWrappedException e) + { + String s = (String)e.WrappedException; + Console.WriteLine(e.Message); + Console.WriteLine(s); + } + } +} diff --git a/Chapter10/serialization.cpp b/Chapter10/serialization.cpp new file mode 100644 index 0000000..f980298 --- /dev/null +++ b/Chapter10/serialization.cpp @@ -0,0 +1,87 @@ +// serialization.cpp +#using "System.Xml.dll" + +using namespace System; +using namespace System::IO; +using namespace System::Xml::Serialization; + +// To participate in serialization, types must be public. +[Serializable] +public enum class SunEnum { FullSun, PartShade, Shade }; + +[Serializable] +public enum class WaterEnum { Moist, Medium, Dry }; + +[ Serializable] +public ref class Plant +{ + // an internal counter to determine instance IDs + static int counter; + + + // The instance ID keeps track of the plant objects. It will be + // a different ID when the object is deserialized, so this does not need + // to be serialized. We use the NonSerialized attribute to indicate that. + [NonSerialized] + int InstanceID; + + public: + property String^ Genus; + property String^ Species; + property String^ Cultivar; + property String^ CommonName; + property SunEnum Sun; + property WaterEnum Water; + property int Zone; + + + + Plant() {} + + Plant(String^ genus, String^ species, String^ commonName, + String^ cultivar, SunEnum sun, WaterEnum water, int zone) + { + Genus = genus; Species = species; Cultivar = cultivar; + Sun = sun; Water = water; Zone = zone; + InstanceID = counter++; + } + + static Plant() { counter = 0; } + +}; + +void CreateAndSerialize(String^ genus, String^ species, String^ commonName, + String^ cultivar, SunEnum sun, WaterEnum water, int zone) +{ + Plant^ p = gcnew Plant(genus, species, commonName, cultivar, sun, + water, zone); + + // The XmlSerializer takes the Type object as a parameter. + XmlSerializer^ serializer = gcnew XmlSerializer(Plant::typeid); + // Create a StreamWriter object to write to a file. + StreamWriter^ sw = gcnew StreamWriter("plants.xml"); + + // Serialize causes the XML to be generated. + serializer->Serialize(sw, p); + sw->Close(); +} + +Plant^ Deserialize() +{ + Plant^ p; + XmlSerializer^ serializer = gcnew XmlSerializer(Plant::typeid); + // To read the file, use a FileStream object. + FileStream^ fs = gcnew FileStream("plants.xml", FileMode::Open); + // Deserialize and cast to object type. + p = safe_cast( serializer->Deserialize(fs) ); + return p; +} + +int main() +{ + CreateAndSerialize("Ampelopsis", "brevipedunculata", + "Porcelain Berry", nullptr, SunEnum::PartShade, WaterEnum::Medium, + 4); + + Deserialize(); +} diff --git a/Chapter10/throw_string.cpp b/Chapter10/throw_string.cpp new file mode 100644 index 0000000..3f81536 --- /dev/null +++ b/Chapter10/throw_string.cpp @@ -0,0 +1,24 @@ +// throw_string.cpp + +using namespace System; + +public ref class R +{ +public: + static void TrySomething() + { + throw gcnew String("Error that throws string!"); + } +}; + +int main() +{ + try + { + R::TrySomething(); + } + catch(String^ s) + { + Console::WriteLine(s); + } +} diff --git a/Chapter11/arraylist.cpp b/Chapter11/arraylist.cpp new file mode 100644 index 0000000..4fb88af --- /dev/null +++ b/Chapter11/arraylist.cpp @@ -0,0 +1,25 @@ +// arraylist.cpp +using namespace System; +using namespace System::Collections; + +int main() +{ + ArrayList^ array_list = gcnew ArrayList(); + + array_list->Add(1); + + array_list->Add("test"); + + // Iterate using the for each operator. + for each (Object^ o in array_list) + { + Console::WriteLine( o->ToString() ); + } + + // Iterate using indexing. + + for (int i = 0; i < array_list->Count; i++) + { + Console::WriteLine("{0} {1}", i, array_list[i]); + } +} diff --git a/Chapter11/assembly1.cpp b/Chapter11/assembly1.cpp new file mode 100644 index 0000000..185a1ca --- /dev/null +++ b/Chapter11/assembly1.cpp @@ -0,0 +1,22 @@ +// assembly1.cpp + +#include "managed_template.h" + +using namespace System; + +public ref class CBridge +{ + public: + + static void F(CTemplate^ ct_int) + { + Console::WriteLine("{0} ", ct_int->InnerObject ); + } +}; + +int main() +{ + CTemplate^ ct_int; + ct_int = gcnew CTemplate(55); + CBridge::F(ct_int); +} diff --git a/Chapter11/assembly2.cpp b/Chapter11/assembly2.cpp new file mode 100644 index 0000000..7a09aae --- /dev/null +++ b/Chapter11/assembly2.cpp @@ -0,0 +1,13 @@ +// assembly2.cpp + +#include "managed_template.h" + +#using "assembly1.dll" + +int main() +{ + CTemplate^ ctemplate_int = gcnew CTemplate(67); + + CBridge^ bridge = gcnew CBridge(); + bridge->F(ctemplate_int); +} diff --git a/Chapter11/assembly2_with_generic.cpp b/Chapter11/assembly2_with_generic.cpp new file mode 100644 index 0000000..d1a5863 --- /dev/null +++ b/Chapter11/assembly2_with_generic.cpp @@ -0,0 +1,14 @@ +// assembly2_with_generic.cpp + +#using "generic_interface.dll" +#using "template_with_generic_interface.dll" + +#include "template_with_generic_interface.h" + +int main() +{ + CTemplate^ ctemplate_int = gcnew CTemplate(67); + + CBridge^ bridge = gcnew CBridge(); + bridge->F(ctemplate_int); +} diff --git a/Chapter11/casting_from_object.cpp b/Chapter11/casting_from_object.cpp new file mode 100644 index 0000000..1723d44 --- /dev/null +++ b/Chapter11/casting_from_object.cpp @@ -0,0 +1,40 @@ +// casting_from_object.cpp +using namespace System; +using namespace System::Collections; + +ref class Book +{ + public: + Book() + { } + Book(String^ _title) { Title = _title; } + property String^ Title; +}; + +int main() +{ + ArrayList^ theList = gcnew ArrayList(); + + theList->Add( gcnew Book("Of Mice and Men") ); + + // Use a cast to retrive an object from the list + // and convert to the appropriate type. + Book^ book = safe_cast( theList[0] ); + + Console::WriteLine("OK. The object was retrieved and the title is " + + book->Title ); + + // Now try putting an object of the wrong type + // in the list and retrieving it using the same + // method. + + theList->Add( gcnew String("bad data")); + try + { + book = safe_cast( theList[1] ); + } + catch(InvalidCastException^ e) + { + Console::WriteLine("An object of the wrong type was put on the list."); + } +} diff --git a/Chapter11/class_constraint.cpp b/Chapter11/class_constraint.cpp new file mode 100644 index 0000000..7d43ef3 --- /dev/null +++ b/Chapter11/class_constraint.cpp @@ -0,0 +1,35 @@ +// class_constraint.cpp +using namespace System; +ref class B +{ + public: + virtual void f() {} +}; + +generic where T : B +ref class G +{ + T t; + + public: + G(T t_in) : t(t_in) + { + // For this example, C::f is + // called. + t->f(); + } +}; + +ref class C : B +{ + public: + virtual void f() override + { + Console::WriteLine("C::f"); + } +}; + +int main() +{ + G^ r = gcnew G(gcnew C()); +} diff --git a/Chapter11/dictionary.cpp b/Chapter11/dictionary.cpp new file mode 100644 index 0000000..031429f --- /dev/null +++ b/Chapter11/dictionary.cpp @@ -0,0 +1,31 @@ +// dictionary.cpp +using namespace System; +using namespace System::Collections::Generic; + +int main() +{ + IDictionary^ dict; + dict = gcnew Dictionary(); + + // The add method takes the key and the value. + dict->Add("hat", "head adornment"); + dict->Add("hot", "at a high temperature"); + dict->Add("hit", "to strike"); + + // Use the KeyValuePair generic class when using the + // for each statement. + for each (KeyValuePair^ pair in dict) + { + Console::WriteLine(" {0}: {1}", pair->Key, pair->Value); + } + + // The remove method takes the key as an argument. + dict->Remove("hat"); + + // Use the KeyValuePair generic class when using the + // for each statement. + for each (KeyValuePair^ pair in dict) + { + Console::WriteLine(" {0}: {1}", pair->Key, pair->Value); + } +} diff --git a/Chapter11/generic_class1.cpp b/Chapter11/generic_class1.cpp new file mode 100644 index 0000000..d9fde8b --- /dev/null +++ b/Chapter11/generic_class1.cpp @@ -0,0 +1,38 @@ +// generic_class1.cpp +using namespace System; + +generic +ref class R +{ + T t; + + public: + + R() {} + + property T InnerValue + { + T get() { return t; } + void set(T value) { t = value; } + } +}; + +int main() +{ + double d = 0.01; + int n = 12; + + // Create an object with T equal to double. + R^ r_double = gcnew R(); + + // Create an object with T equal to int. + R^ r_int = gcnew R(); + + r_double->InnerValue = d; + + r_int->InnerValue = n; + + Console::WriteLine( r_double->InnerValue ); + + Console::WriteLine( r_int->InnerValue ); +} diff --git a/Chapter11/generic_function.cpp b/Chapter11/generic_function.cpp new file mode 100644 index 0000000..9d88e50 --- /dev/null +++ b/Chapter11/generic_function.cpp @@ -0,0 +1,8 @@ +// generic_function.cpp +using namespace System; + +generic +void F(T t, int i, String^ s) +{ + // ... +} diff --git a/Chapter11/generic_functions1.cpp b/Chapter11/generic_functions1.cpp new file mode 100644 index 0000000..c26e8d3 --- /dev/null +++ b/Chapter11/generic_functions1.cpp @@ -0,0 +1,19 @@ +// generic_functions1.cpp +using namespace System; + +generic < typename T> +void GenericFunction(T t) +{ + Console::WriteLine(t); +} + +int main() +{ + int i; + // Specify the type parameter. + GenericFunction( 200 ); + + // Allow the type parameter to be + // deduced. + GenericFunction( 400 ); +} diff --git a/Chapter11/generic_functions2.cpp b/Chapter11/generic_functions2.cpp new file mode 100644 index 0000000..885d0b2 --- /dev/null +++ b/Chapter11/generic_functions2.cpp @@ -0,0 +1,23 @@ +// generic_functions2.cpp +using namespace System; + +generic < typename T> +void GenericFunction(array^ array_of_t) +{ + for each (T t in array_of_t) + { + Console::WriteLine(t); + } +} + +int main() +{ + array^ array_of_string; + + array_of_string = gcnew array + { "abc", "def", "ghi" }; + + // Allow the type parameter to be + // deduced. + GenericFunction( array_of_string ); +} diff --git a/Chapter11/generic_gcnew.cpp b/Chapter11/generic_gcnew.cpp new file mode 100644 index 0000000..05dd5ca --- /dev/null +++ b/Chapter11/generic_gcnew.cpp @@ -0,0 +1,21 @@ +// generic_gcnew.cpp +using namespace System; + +generic where T: gcnew() +T CreateInstance() +{ + return gcnew T(); +} + +ref class R +{ + public: + + R() { } +}; + +int main() +{ + int i = CreateInstance(); + R^ r = CreateInstance(); +} diff --git a/Chapter11/generic_interface.cpp b/Chapter11/generic_interface.cpp new file mode 100644 index 0000000..dae19d2 --- /dev/null +++ b/Chapter11/generic_interface.cpp @@ -0,0 +1,10 @@ +// generic_interface.cpp +// Declare your generic interfaces and compile to a DLL. +// Reference the compiled assembly using #using. +// Do not reference the source as an included file. + +generic +public interface class IGInterface +{ + property T InnerObject; +}; diff --git a/Chapter11/generic_list.cpp b/Chapter11/generic_list.cpp new file mode 100644 index 0000000..5ceec45 --- /dev/null +++ b/Chapter11/generic_list.cpp @@ -0,0 +1,196 @@ +// generic_list.cpp +using namespace System; +using namespace System::Collections::Generic; + +// ListNode represents a single element in a linked list. +generic ref struct ListNode +{ + ListNode(T t) : item(t) { } + + // The item field represents the data in the list. + T item; + // The next node in the list; + ListNode^ next; +}; + +// List represents a linked list. +generic ref class MyList : IEnumerable^> +{ + ListNode^ first; + + public: + + property bool changed; + + // Add an item to the end of the list. + void Add(T t) + { + changed = true; + if (first == nullptr) + { + first = gcnew ListNode(t); + } + else + { + // Find the end. + ListNode^ node = first; + while (node->next != nullptr) + { + node = node->next; + } + node->next = gcnew ListNode(t); + } + } + + // Return true if the object was removed, + // false if it was not found. + bool Remove(T t) + { + changed = true; + if (first == nullptr) + return false; + if (first->item->Equals(t)) + { + // remove first from list by + // resetting first + first = first->next; + return true; + } + ListNode^ node = first; + while(node->next != nullptr) + { + if (node->next->item->Equals(t)) + { + // delete node->next->item; + // Remove next from list by + // leapfrogging it. + node->next = node->next->next; + return true; + } + node = node->next; + } + return false; + } + + property ListNode^ First + { + ListNode^ get() + { + return first; + } + } + + private: + virtual System::Collections::IEnumerator^ GetEnumerator_NG() sealed + = System::Collections::IEnumerable::GetEnumerator + { + return GetEnumerator(); + } + + virtual IEnumerator^>^ GetEnumerator_G() sealed + = IEnumerable^>::GetEnumerator + { + return GetEnumerator(); + } + + public: + IEnumerator^>^ GetEnumerator() + { + ListEnumerator^ enumerator = gcnew ListEnumerator(this); + return (IEnumerator^>^) enumerator; + } + + // ListEnumerator is a struct that walks the list, pointing + // to each element in turn. + generic ref struct ListEnumerator : IEnumerator^> + { + ListNode^ current; + MyList^ theList; + bool beginning; + + ListEnumerator(MyList^ list) : theList(list), beginning(true) + { + theList->changed = false; + } + + private: + virtual property Object^ Current_NG + { + Object^ get() sealed = + System::Collections::IEnumerator::Current::get + { + return (Object^) Current; + } + } + + virtual property ListNode^ Current_G + { + ListNode^ get() sealed = IEnumerator^>::Current::get + { + return Current; + } + } + + public: + + property ListNode^ Current + { + ListNode^ get() + { + if (theList->changed) + throw gcnew InvalidOperationException(); + return current; + } + } + + virtual bool MoveNext() + { + if (theList->changed) + throw gcnew InvalidOperationException(); + beginning = false; + if (current != nullptr) + { + current = current->next; + } + else + current = theList->First; + + if (current != nullptr) + return true; + else + return false; + } + + virtual void Reset() + { + theList->changed = false; + current = theList->First; + } + + ~ListEnumerator() {} + + }; // end of ListEnumerator +}; // end of MyList + +int main() +{ + MyList^ int_list = gcnew MyList(); + + int_list->Add(10); + int_list->Add(100); + int_list->Add(1000); + int_list->Add(100000); + int_list->Add(500); + int_list->Remove(10); + int_list->Remove(1000); + int_list->Remove(500); + int_list->Add(50); + + // Iterate through the list using the for each statement, + // displaying each member of the list at the console. + for each (ListNode^ node in int_list) + { + Console::WriteLine(node->item); + // int_list->Remove(50); // danger: modifying the collection + } +} diff --git a/Chapter11/generic_list2.cpp b/Chapter11/generic_list2.cpp new file mode 100644 index 0000000..5b4986a --- /dev/null +++ b/Chapter11/generic_list2.cpp @@ -0,0 +1,196 @@ +// generic_list.cpp +using namespace System; +using namespace System::Collections::Generic; + +// ListNode represents a single element in a linked list. +generic ref struct ListNode +{ + ListNode(T t) : item(t) { } + + // The item field represents the data in the list. + T item; + // The next node in the list; + ListNode^ next; +}; + +// List represents a linked list. +generic ref class MyList : IEnumerable^> +{ + ListNode^ first; + + public: + + property bool changed; + + // Add an item to the end of the list. + void Add(T t) + { + changed = true; + if (first == nullptr) + { + first = gcnew ListNode(t); + } + else + { + // Find the end. + ListNode^ node = first; + while (node->next != nullptr) + { + node = node->next; + } + node->next = gcnew ListNode(t); + } + } + + // Return true if the object was removed, + // false if it was not found. + bool Remove(T t) + { + changed = true; + if (first == nullptr) + return false; + if (first->item->Equals(t)) + { + // remove first from list by + // resetting first + first = first->next; + return true; + } + ListNode^ node = first; + while(node->next != nullptr) + { + if (node->next->item->Equals(t)) + { + delete node->next->item; + // Remove next from list by + // leapfrogging it. + node->next = node->next->next; + return true; + } + node = node->next; + } + return false; + } + + property ListNode^ First + { + ListNode^ get() + { + return first; + } + } + + private: + virtual System::Collections::IEnumerator^ GetEnumerator_NG() sealed + = System::Collections::IEnumerable::GetEnumerator + { + return GetEnumerator(); + } + + virtual IEnumerator^>^ GetEnumerator_G() sealed + = IEnumerable^>::GetEnumerator + { + return GetEnumerator(); + } + + public: + IEnumerator^>^ GetEnumerator() + { + ListEnumerator^ enumerator = gcnew ListEnumerator(this); + return (IEnumerator^>^) enumerator; + } + + // ListEnumerator is a struct that walks the list, pointing + // to each element in turn. + generic ref struct ListEnumerator : IEnumerator^> + { + ListNode^ current; + MyList^ theList; + bool beginning; + + ListEnumerator(MyList^ list) : theList(list), beginning(true) + { + theList->changed = false; + } + + private: + virtual property Object^ Current_NG + { + Object^ get() sealed = + System::Collections::IEnumerator::Current::get + { + return (Object^) Current; + } + } + + virtual property ListNode^ Current_G + { + ListNode^ get() sealed = IEnumerator^>::Current::get + { + return Current; + } + } + + public: + + property ListNode^ Current + { + ListNode^ get() + { + if (theList->changed) + throw gcnew InvalidOperationException(); + return current; + } + } + + virtual bool MoveNext() + { + if (theList->changed) + throw gcnew InvalidOperationException(); + beginning = false; + if (current != nullptr) + { + current = current->next; + } + else + current = theList->First; + + if (current != nullptr) + return true; + else + return false; + } + + virtual void Reset() + { + theList->changed = false; + current = theList->First; + } + + ~ListEnumerator() {} + + }; // end of ListEnumerator +}; // end of MyList + +int main() +{ + MyList^ int_list = gcnew MyList(); + + int_list->Add(10); + int_list->Add(100); + int_list->Add(1000); + int_list->Add(100000); + int_list->Add(500); + int_list->Remove(10); + int_list->Remove(1000); + int_list->Remove(500); + int_list->Add(50); + + // Iterate through the list using the for each statement, + // displaying each member of the list at the console. + for each (ListNode^ node in int_list) + { + Console::WriteLine(node->item); + // int_list->Remove(50); // danger: modifying the collection + } +} diff --git a/Chapter11/generic_list_strings.cpp b/Chapter11/generic_list_strings.cpp new file mode 100644 index 0000000..e648e26 --- /dev/null +++ b/Chapter11/generic_list_strings.cpp @@ -0,0 +1,204 @@ +// generic_list_strings.cpp +using namespace System; +using namespace System::Collections::Generic; + +using namespace System; +using namespace System::Collections::Generic; + +// ListNode represents a single element in a linked list. +generic ref struct ListNode +{ + ListNode(T t) : item(t) { } + + // The item field represents the data in the list. + T item; + // The next node in the list; + ListNode^ next; +}; + +// List represents a linked list. +generic ref class MyList : IEnumerable^> +{ + ListNode^ first; + + public: + + property bool changed; + + // Add an item to the end of the list. + void Add(T t) + { + changed = true; + if (first == nullptr) + { + first = gcnew ListNode(t); + } + else + { + // Find the end. + ListNode^ node = first; + while (node->next != nullptr) + { + node = node->next; + } + node->next = gcnew ListNode(t); + } + } + + // Return true if the object was removed, + // false if it was not found. + bool Remove(T t) + { + changed = true; + if (first == nullptr) + return false; + if (first->item->Equals(t)) + { + // remove first from list by + // resetting first + first = first->next; + return true; + } + ListNode^ node = first; + while(node->next != nullptr) + { + if (node->next->item->Equals(t)) + { + // Remove next from list by + // leapfrogging it. + node->next = node->next->next; + return true; + } + node = node->next; + } + return false; + } + + property ListNode^ First + { + ListNode^ get() + { + return first; + } + } + + private: + virtual System::Collections::IEnumerator^ GetEnumerator_NG() sealed + = System::Collections::IEnumerable::GetEnumerator + { + return GetEnumerator(); + } + + virtual IEnumerator^>^ GetEnumerator_G() sealed + = IEnumerable^>::GetEnumerator + { + return GetEnumerator(); + } + + public: + IEnumerator^>^ GetEnumerator() + { + ListEnumerator^ enumerator = gcnew ListEnumerator(this); + return (IEnumerator^>^) enumerator; + } + + // ListEnumerator is a struct that walks the list, pointing + // to each element in turn. + generic ref struct ListEnumerator : IEnumerator^> + { + ListNode^ current; + MyList^ theList; + bool beginning; + + ListEnumerator(MyList^ list) : theList(list), beginning(true) + { + theList->changed = false; + } + + private: + virtual property Object^ Current_NG + { + Object^ get() sealed = + System::Collections::IEnumerator::Current::get + { + return (Object^) Current; + } + } + + virtual property ListNode^ Current_G + { + ListNode^ get() sealed = IEnumerator^>::Current::get + { + return Current; + } + } + + public: + + property ListNode^ Current + { + ListNode^ get() + { + if (theList->changed) + throw gcnew InvalidOperationException(); + return current; + } + } + + virtual bool MoveNext() + { + if (theList->changed) + throw gcnew InvalidOperationException(); + beginning = false; + if (current != nullptr) + { + current = current->next; + } + else + current = theList->First; + + if (current != nullptr) + return true; + else + return false; + } + + virtual void Reset() + { + theList->changed = false; + current = theList->First; + } + + ~ListEnumerator() {} + + }; // end of ListEnumerator +}; // end of MyList + + +ref class R +{ + String^ name; + + public: + + R(String^ n) : name(n) {} + + virtual String^ ToString() override + { + return name; + } +}; + +int main() +{ + MyList^ R_list = gcnew MyList(); + + R_list->Add(gcnew R("test1")); + R_list->Add(gcnew R("test2")); + R_list->Add(gcnew R("test3")); + + for each (ListNode^ node in R_list) + { + Console::WriteLine(node->item); + } +} diff --git a/Chapter11/generic_multiple_constraints.cpp b/Chapter11/generic_multiple_constraints.cpp new file mode 100644 index 0000000..7c4e568 --- /dev/null +++ b/Chapter11/generic_multiple_constraints.cpp @@ -0,0 +1,27 @@ +// generic_multiple_constraints.cpp +using namespace System; + +interface class I; +ref class C; + +// T must have a public default constructor and +// T must inherit from C and +// T must implement I. +generic +where T : gcnew(), C, I +void F(T t) +{ + // ... +} + +interface class IKey; + +// Use multiple where clauses to specify +// constraints for multiple type parameters. +generic +where Key : IKey +where Value : value class +ref class Dictionary +{ + // ... +}; diff --git a/Chapter11/generic_reference_syntax.cpp b/Chapter11/generic_reference_syntax.cpp new file mode 100644 index 0000000..3a9afb5 --- /dev/null +++ b/Chapter11/generic_reference_syntax.cpp @@ -0,0 +1,29 @@ +// generic_reference_syntax.cpp + +interface class I { void F(); }; + +value struct V : I { virtual void F() {} }; + +ref struct R : I { virtual void F() {} }; + +generic where T : I +ref class G +{ + T t; + + public: + G(T t) + { + // The handle syntax -> is used + // even though T could be a value type. + t->F(); + } +}; + +int main() +{ + V v; + R^ r = gcnew R(); + G^ gv = gcnew G(v); + G^ gr = gcnew G(r); +} diff --git a/Chapter11/generic_returnvalue.cpp b/Chapter11/generic_returnvalue.cpp new file mode 100644 index 0000000..1c39eb5 --- /dev/null +++ b/Chapter11/generic_returnvalue.cpp @@ -0,0 +1,15 @@ +// generic_return_value.cpp +using namespace System; + +generic +T f() +{ + return T(); +} + +int main() +{ + int i = f(); // OK + String^ s = f(); // OK + double d = f(); // Error! Can't deduce type. +} diff --git a/Chapter11/iaddition.cpp b/Chapter11/iaddition.cpp new file mode 100644 index 0000000..72775aa --- /dev/null +++ b/Chapter11/iaddition.cpp @@ -0,0 +1,10 @@ +interface class IAddition +{ + static IAddition^ operator+(IAddition^, IAddition^); +}; + +generic where T : IAddition +ref class G +{ + IAddition^ add(T t1, T t2) { return t1 + t2; } +}; diff --git a/Chapter11/interface_constraint.cpp b/Chapter11/interface_constraint.cpp new file mode 100644 index 0000000..6344787 --- /dev/null +++ b/Chapter11/interface_constraint.cpp @@ -0,0 +1,37 @@ +// interface_constraint.cpp + +interface class I +{ + void f(); +}; + +// The constraint is introduced with the where keyword +// and requires that T inherit from I. +generic where T : I +ref class R +{ + T t; + + public: + R(T t_in) : t(t_in) + { + // Call the method on I. + // This code would not compile without + // the constraint. + t->f(); + } +}; + +ref class C : I +{ + public: + virtual void f() + { + // ... + } +}; + +int main() +{ + R^ r = gcnew R(gcnew C()); +} diff --git a/Chapter11/invalid_use_of_generic_param.cpp b/Chapter11/invalid_use_of_generic_param.cpp new file mode 100644 index 0000000..9b8c083 --- /dev/null +++ b/Chapter11/invalid_use_of_generic_param.cpp @@ -0,0 +1,17 @@ +// invalid_use_of_type_param.cpp + +generic +ref class G +{ + T t; + + public: + + G() + { + t = gcnew T("abc", 100); // Error: T may not have + // a compatible constructor. + t->F(); // Error: T may not have F. + } + +}; diff --git a/Chapter11/list_generic.cpp b/Chapter11/list_generic.cpp new file mode 100644 index 0000000..394f490 --- /dev/null +++ b/Chapter11/list_generic.cpp @@ -0,0 +1,25 @@ +// list_generic.cpp +using namespace System; +using namespace System::Collections::Generic; + +int main() +{ + List^ list = gcnew List(); + // or IList^ list = gcnew List(); + + list->Add("apple"); + list->Add("banana"); + + // Iterate using the for each operator. + for each (String^ s in list) + { + Console::WriteLine( s ); + } + + // Iterate using indexing. + + for (int i = 0; i < list->Count; i++) + { + Console::WriteLine("{0} {1}", i, list[i]); + } +} diff --git a/Chapter11/managed_template.h b/Chapter11/managed_template.h new file mode 100644 index 0000000..f4f4fc3 --- /dev/null +++ b/Chapter11/managed_template.h @@ -0,0 +1,18 @@ +// managed_template.h + +template +public ref class CTemplate +{ + T m_obj; + + public: + + CTemplate(T obj) { m_obj = obj; } + + property T InnerObject + { + T get() { return m_obj; } + void set(T obj) { m_obj = obj; } + } + +}; diff --git a/Chapter11/managed_templates.cpp b/Chapter11/managed_templates.cpp new file mode 100644 index 0000000..3307340 --- /dev/null +++ b/Chapter11/managed_templates.cpp @@ -0,0 +1,18 @@ +// managed_templates.cpp + +#include "managed_template.h" + +using namespace System; + +int main() +{ + CTemplate^ ct_int; + CTemplate^ ct_string; + + ct_int = gcnew CTemplate(55); + ct_string = gcnew CTemplate("test"); + + Console::WriteLine("{0} ", ct_int->InnerObject ); + Console::WriteLine("{0} ", ct_string->InnerObject ); + +} diff --git a/Chapter11/multiple_generic_parameters.cpp b/Chapter11/multiple_generic_parameters.cpp new file mode 100644 index 0000000..963392c --- /dev/null +++ b/Chapter11/multiple_generic_parameters.cpp @@ -0,0 +1,8 @@ +// multiple_generic_parameters.cpp +using namespace System; + +generic +void F(T t, array^ a, int i, String^ s) +{ + // ... +} diff --git a/Chapter11/operator_plus.cpp b/Chapter11/operator_plus.cpp new file mode 100644 index 0000000..d78c6f4 --- /dev/null +++ b/Chapter11/operator_plus.cpp @@ -0,0 +1,6 @@ +template +ref class A +{ + // assumes T supports the + operator + T add(T t1, T t2) { return t1 + t2; } +}; diff --git a/Chapter11/template_with_generic_interface.cpp b/Chapter11/template_with_generic_interface.cpp new file mode 100644 index 0000000..d28f1cf --- /dev/null +++ b/Chapter11/template_with_generic_interface.cpp @@ -0,0 +1,15 @@ +// template_with_generic_interface.cpp + +#include "template_with_generic_interface.h" + +using namespace System; + +public ref class CBridge +{ + public: + + static void F(IGInterface^ ct_int) + { + Console::WriteLine("{0} ", ct_int->InnerObject ); + } +}; diff --git a/Chapter11/template_with_generic_interface.h b/Chapter11/template_with_generic_interface.h new file mode 100644 index 0000000..eb36aeb --- /dev/null +++ b/Chapter11/template_with_generic_interface.h @@ -0,0 +1,20 @@ +// template_with_generic_interface.h + +#using "generic_interface.dll" + +template +ref class CTemplate : IGInterface +{ + T m_obj; + + public: + + CTemplate(T obj) { m_obj = obj; } + + virtual property T InnerObject + { + T get() { return m_obj; } + void set(T obj) { m_obj = obj; } + } + +}; diff --git a/Chapter11/valuetype_constraint.cpp b/Chapter11/valuetype_constraint.cpp new file mode 100644 index 0000000..2bca0c1 --- /dev/null +++ b/Chapter11/valuetype_constraint.cpp @@ -0,0 +1,6 @@ +// valuetype_constraint.cpp + +generic +where T : value class +ref class G +{ /* ... */ }; diff --git a/Chapter12/TaxonLibrary.h b/Chapter12/TaxonLibrary.h new file mode 100644 index 0000000..f7e234b --- /dev/null +++ b/Chapter12/TaxonLibrary.h @@ -0,0 +1,100 @@ +// TaxonLibrary.h +#include + +using namespace System; +using namespace cliext; + +public ref class Taxon abstract +{ + public: + + Taxon() {} + + // copy constructor + Taxon(Taxon% taxon_in) { } + + // destructor + virtual ~Taxon() {} + + // assignment operator + Taxon% operator=(Taxon% t) { return *this; } + + // multimap requires the < operator for its sorting operations + virtual bool operator <(Taxon^ right) + { + return Name < right->Name; + } + + property String^ Name; +}; + +public ref class Genus : public Taxon +{ + public: + + Genus() { Name = ""; } + Genus(String^ name_in) { Name = name_in; } + Genus(Genus% genus_in) { Name = genus_in.Name; } + Genus% operator=(Genus% g) { Name = g.Name; return *this; } + virtual ~Genus() {} +}; + +public ref class Species : public Taxon +{ +public: + Species() { Name = ""; } + Species(String^ name_in) { Name = name_in; } + Species(Species% species_in) { Name = species_in.Name; } + Species% operator=(Species% s) { Name = s.Name; return *this; } + virtual ~Species() {} +}; + +typedef multimap SpeciesMap1; +typedef multimap SpeciesMap2; + +public ref class Trees +{ + SpeciesMap1^ speciesMap1; + public: + + Trees() + { + speciesMap1 = gcnew SpeciesMap1(); + } + + bool Add(Genus^ genus, Species^ species) + { + speciesMap1->insert (make_pair(genus, species)); + return true; + } + + // Iterate over the collection using STL/CLR iterators + void PrintAll() + { + SpeciesMap1::iterator iter1; + for (iter1 = speciesMap1->begin(); iter1 != speciesMap1->end(); ++iter1) + { + Console::WriteLine("{0} {1}", iter1->first->Name, iter1->second->Name); + } + } + + // Iterate over the collection using generic iterators + void PrintAllGeneric() + { + // Declare a generic iterator for this collection + SpeciesMap1::generic_iterator generic_iter1; + + // Get a generic version of the container using the generic_container typedef + // and using the implicit conversion from the container to + // the generic_container type + SpeciesMap1::generic_container^ generic_container1 = speciesMap1; + for (generic_iter1 = generic_container1->begin(); + generic_iter1 != generic_container1->end(); + ++generic_iter1) + { + SpeciesMap1::generic_value val = *generic_iter1; + Console::WriteLine("{0} {1}", generic_iter1->first->Name, generic_iter1->second->Name); + } + } + +}; diff --git a/Chapter12/Taxonomy.cpp b/Chapter12/Taxonomy.cpp new file mode 100644 index 0000000..e6eb48e --- /dev/null +++ b/Chapter12/Taxonomy.cpp @@ -0,0 +1,15 @@ +// Taxonomy.cpp : main project file. + +#include "TaxonLibrary.h" +using namespace System; + +int main(array ^args) +{ + Trees^ trees = gcnew Trees(); + trees->Add(gcnew Genus("Quercus"), gcnew Species("rubra")); + trees->Add(gcnew Genus("Quercus"), gcnew Species("garryana")); + trees->Add(gcnew Genus("Pseudotsuga"), gcnew Species("menziesii")); + // print, using normal STL/CLR iterators + trees->PrintAll(); + return 0; +} diff --git a/Chapter12/card_deque.cpp b/Chapter12/card_deque.cpp new file mode 100644 index 0000000..a1e832b --- /dev/null +++ b/Chapter12/card_deque.cpp @@ -0,0 +1,2 @@ +// card_deque.cpp +#include "card_deque.h" diff --git a/Chapter12/card_deque.h b/Chapter12/card_deque.h new file mode 100644 index 0000000..e781a3c --- /dev/null +++ b/Chapter12/card_deque.h @@ -0,0 +1,150 @@ +// card_deque.h + +#include +#include // for random_shuffle and rotate +#include // for rand +#include // for floor + +using namespace cliext; + +using namespace System; +using namespace System::Text; +using namespace System::Collections; + +enum class SuitEnum { Diamonds, Clubs, Hearts, Spades }; + +// represents a playing card +public value struct Card +{ + SuitEnum suit; + unsigned int rank; + literal int CHAR_HEART = 3; // ANSI heart character + literal int CHAR_DIAMOND = 4; // ANSI diamond + literal int CHAR_CLUB = 5; // ANSI club + literal int CHAR_SPADE = 6; // ANSI spade + + Card(SuitEnum s, int r) : suit(s), rank(r) {} + + // operator== is required for deque on a value type + bool operator==(Card% c) + { return c.rank == rank && c.suit == suit; } + + // Render the two-character card using ANSI card values. + virtual String^ ToString() override + { + + if (rank <= 0 || rank > 13) + throw gcnew ArgumentOutOfRangeException(); + + StringBuilder^ s = gcnew StringBuilder(); + if (rank > 1 && rank < 11) + { + s->Append(rank); + } + else + { + switch (rank) + { + case 1: // Ace + s->Append("A"); + case 11: // Jack + s->Append("J"); + break; + case 12: // Queen + s->Append("Q"); + break; + case 13: // King + s->Append("K"); + break; + default: + throw gcnew ArgumentOutOfRangeException(); + } + } + switch (suit) + { + case SuitEnum::Clubs: + s->Append(CHAR_CLUB, 1); + break; + case SuitEnum::Hearts: + s->Append(CHAR_HEART, 1); + break; + case SuitEnum::Diamonds: + s->Append(CHAR_DIAMOND, 1); + break; + case SuitEnum::Spades: + s->Append(CHAR_SPADE, 1); + break; + default: + throw gcnew ArgumentOutOfRangeException(); + } + return s->ToString(); + } +}; + +typedef deque deck_type; +typedef deck_type::iterator deck_iterator; + +public ref class Deck +{ + deck_type m_deck; + static int seed; + static int m_random; + + public: + + Deck() + { + Reset(); + } + + // Get a card from the front of the deck. + Card^ DrawFirst() + { + if ( m_deck.size() > 0) + { + Card^ pCard = m_deck.front(); + m_deck.pop_front(); + return pCard; + } + else + { + return nullptr; + } + } + + Card* Peek(int nCard) + { + return &m_deck[nCard]; + } + + // Shuffle the deck + void Shuffle() + { + // Use the STL/CLR algorithm random_shuffle + random_shuffle( m_deck.begin(), m_deck.end() ); + } + + // Reset to the original unshuffled order + // in a standard deck + void Reset() + { + int s; + int rank; + m_deck.clear(); + for (s = 0; s <= 3; ++s) + { + for (rank = 1; rank <= 13; ++rank) + { + m_deck.push_back(Card( safe_cast(s), rank)); + } + } + } + + // Cut the deck: put the bottom cards at the top of the deck. + // This is equivalent to a rotate operation + void Cut(int n) + { + rotate(m_deck.begin(), m_deck.begin() + n, m_deck.end()); + } + +}; diff --git a/Chapter12/cards_main.cpp b/Chapter12/cards_main.cpp new file mode 100644 index 0000000..35963d0 --- /dev/null +++ b/Chapter12/cards_main.cpp @@ -0,0 +1,14 @@ +// cards_main.cpp +#include "card_deque.h" +int main() +{ + Deck deck; + deck.Shuffle(); + // cut the deck at card 40 + deck.Cut(40); + + Card c1 = *deck.DrawFirst(); + //cout << "Your draw is " << c1 << endl; + Console::WriteLine("Your draw is {0}", c1); + + } diff --git a/Chapter12/consume_fromcs.cs b/Chapter12/consume_fromcs.cs new file mode 100644 index 0000000..c007b6b --- /dev/null +++ b/Chapter12/consume_fromcs.cs @@ -0,0 +1,22 @@ +// consume_fromcs.cs +// csc /r:stl_exposedtocs.dll consume_fromcs.cs + +using N; +using System.Collections.Generic; + +class C +{ + public static void Main() + { + R r; + r = new R(20); + + IList list_dotnet = r.Vec; + foreach (int i in list_dotnet) + { + System.Console.Write( i ); + } + System.Console.WriteLine(); + } + +} diff --git a/Chapter12/generic_method_on_stlclr_collection.cpp b/Chapter12/generic_method_on_stlclr_collection.cpp new file mode 100644 index 0000000..80ef1ef --- /dev/null +++ b/Chapter12/generic_method_on_stlclr_collection.cpp @@ -0,0 +1,40 @@ +// generic_method_with_stlclr_collection.cpp + +#include + +using namespace System; +using namespace System::Collections::Generic; +using namespace cliext; + +generic +where CollectionType : IList +void Reverse(CollectionType collection) +{ + + int n = collection->Count; + for (int i = 0; i < n; i++) + { + ItemType temp = collection->default[n - i]; + collection->RemoveAt(n - i); + collection->Add(temp); + } + +} + +int main() +{ + vector^ v = gcnew vector(10); + + for (int i = 0; i < 10; i++) + { + (*v)[i] = i; + } + + Reverse^, int>(v); + + for each (int i in v) + { + Console::Write("{0} ", i); + } + +} diff --git a/Chapter12/interface_method_on_stlclr_collection.cpp b/Chapter12/interface_method_on_stlclr_collection.cpp new file mode 100644 index 0000000..a9ea8b2 --- /dev/null +++ b/Chapter12/interface_method_on_stlclr_collection.cpp @@ -0,0 +1,28 @@ +// interface_method_on_stlclr_collection.cpp + + +#include + +using namespace cliext; +using namespace System; +using namespace System::Collections::Generic; + +int main() +{ + vector^ v = gcnew vector(10); + + for (int i = 0; i < 10; i++) + { + (*v)[i] = i; + } + + ICollection^ collection = (ICollection^) v; + + collection->Add(11); // OK + + + for each (int i in collection) + { + Console::Write("{0} ", i); + } +} diff --git a/Chapter12/interface_method_on_stlclr_container.cpp b/Chapter12/interface_method_on_stlclr_container.cpp new file mode 100644 index 0000000..01d484f --- /dev/null +++ b/Chapter12/interface_method_on_stlclr_container.cpp @@ -0,0 +1,18 @@ +// interface_method_on_stlclr_container.cpp +#include +using namespace cliext; +using namespace System; + +int main() +{ + vector^ v = gcnew vector(10); + for (int i = 0; i < 10; i++) + { + (*v)[i] = i; + } + v->Add(11); // Error: candidate function not accessible + for each (int i in v) + { + Console::Write("{0} ", i); + } +} diff --git a/Chapter12/stl_simple1.cpp b/Chapter12/stl_simple1.cpp new file mode 100644 index 0000000..fe096a3 --- /dev/null +++ b/Chapter12/stl_simple1.cpp @@ -0,0 +1,26 @@ +// stl_simple1.cpp + +#include +#include +#include + +using namespace std; + +typedef list StringList; +typedef StringList::iterator StringListIter; + +int main() +{ + StringList list; + StringListIter iter; + + list.insert( list.end(), "first" ); + list.insert( list.end(), "second" ); + list.insert( list.end(), "third" ); + + for (iter = list.begin(); iter != list.end(); iter++) + { + cout << *iter << endl; + } + +} diff --git a/Chapter12/stl_simple2.cpp b/Chapter12/stl_simple2.cpp new file mode 100644 index 0000000..57e9a10 --- /dev/null +++ b/Chapter12/stl_simple2.cpp @@ -0,0 +1,25 @@ +// stl_simple2.cpp + +#include + +using namespace std; +using namespace System; + +typedef list StringList; +typedef StringList::iterator StringListIter; + +int main() +{ + StringList list; + StringListIter iter; + + list.insert( list.end(), "first" ); + list.insert( list.end(), "second" ); + list.insert( list.end(), "third" ); + + for (iter = list.begin(); iter != list.end(); iter++) + { + Console::WriteLine( *iter ); + } + +} diff --git a/Chapter12/stl_simple3.cpp b/Chapter12/stl_simple3.cpp new file mode 100644 index 0000000..31c28d1 --- /dev/null +++ b/Chapter12/stl_simple3.cpp @@ -0,0 +1,24 @@ +// stl_simple3.cpp + +#include + +using namespace cliext; +using namespace System; + +typedef list StringList; +typedef StringList::iterator StringListIter; + +int main() +{ + StringList list; + StringListIter iter; + + list.insert( list.end(), "first" ); + list.insert( list.end(), "second" ); + list.insert( list.end(), "third" ); + + for (iter = list.begin(); iter != list.end(); iter++) + { + Console::WriteLine( *iter ); + } +} diff --git a/Chapter12/stlclr_algorithm_on_net_collection.cpp b/Chapter12/stlclr_algorithm_on_net_collection.cpp new file mode 100644 index 0000000..fa65625 --- /dev/null +++ b/Chapter12/stlclr_algorithm_on_net_collection.cpp @@ -0,0 +1,40 @@ +// stlclr_algorithm_on_net_collection.cpp + +#include +#include + +using namespace cliext; +using namespace System; +using namespace System::Collections::Generic; + +typedef collection_adapter> AdapterType; +typedef AdapterType::iterator AdapterIter; + +// function object +ref class Print +{ + + public: + + Print() {} + + Print(Print% p) {} + + void operator()(KeyValuePair% kvp) + { + Console::WriteLine("{0} {1} ", kvp.Key, kvp.Value ); + } +}; + +int main() +{ + IDictionary^ dict = gcnew Dictionary(); + + dict->Add( "cat", "small furry animal"); + dict->Add( "dog", "medium-size friendly animal"); + dict->Add( "goat", "large cantankerous animal"); + dict->Add( "turtle", "small reclusive reptile"); + + AdapterType adapter1(dict); + for_each(adapter1.begin(), adapter1.end(), Print() ); +} diff --git a/Chapter12/stlclr_convert_from_net_to_stlclr.cpp b/Chapter12/stlclr_convert_from_net_to_stlclr.cpp new file mode 100644 index 0000000..0b7afd2 --- /dev/null +++ b/Chapter12/stlclr_convert_from_net_to_stlclr.cpp @@ -0,0 +1,35 @@ +// stlclr_converting_from_net_to_stlclr.cpp + +#include +#include + +using namespace cliext; +using namespace System; +using namespace System::Collections::Generic; + +typedef map MapType; +typedef MapType::iterator MapIter; +typedef collection_adapter> AdapterType; +typedef AdapterType::iterator AdapterIter; + +int main() +{ + IDictionary^ dict = gcnew Dictionary(); + MapType myMap; + + dict->Add( "cat", "small furry animal"); + dict->Add( "dog", "medium-size friendly animal"); + dict->Add( "goat", "large cantankerous animal"); + + AdapterType adapter1(dict); + + for (AdapterIter iter = adapter1.begin(); iter != adapter1.end(); ++iter) + { + myMap.insert(make_pair((*iter).Key, (*iter).Value)); + } + + for (MapIter iter = myMap.begin(); iter != myMap.end(); ++iter) + { + Console::WriteLine("{0} {1} ", (*iter)->first, (*iter)->second ); + } +} diff --git a/Chapter12/stlclr_converting_from_net_to_stlclr.cpp b/Chapter12/stlclr_converting_from_net_to_stlclr.cpp new file mode 100644 index 0000000..e69de29 diff --git a/Chapter12/stlclr_custom_functor.cpp b/Chapter12/stlclr_custom_functor.cpp new file mode 100644 index 0000000..1a08373 --- /dev/null +++ b/Chapter12/stlclr_custom_functor.cpp @@ -0,0 +1,62 @@ +// stlclr_custom_functor.cpp + +#include +#include +#include +#include + + +using namespace cliext; +using namespace System; + +// Function object for printing the value in a single value (nonassociative) collection +template +ref class Print1 +{ + + public: + + Print1() {} + + Print1(Print1% p) {} + + void operator()(T t) + { + Console::Write("{0} ", t ); + } +}; + +// Function object for printing a pair of values in an associative collection +template +ref class Print2 +{ + + public: + + Print2() {} + + Print2(Print2% p) {} + + void operator()(typename map::generic_value p) + { + Console::WriteLine("{0} {1} ", p->first, p->second ); + } +}; + +int main() +{ + vector^ sqrvec = gcnew vector; + map^ sqrtmap = gcnew map; + + for (int i = 1; i < 10; i++) + { + sqrvec->push_back( i*i ); + sqrtmap->insert( make_pair( (double) i, sqrtf(i))); + } + + for_each(sqrvec->begin(), sqrvec->end(), Print1() ); + Console::WriteLine(); + + for_each(sqrtmap->begin(), sqrtmap->end(), Print2() ); + +} diff --git a/Chapter12/stlclr_exposedtocs.cpp b/Chapter12/stlclr_exposedtocs.cpp new file mode 100644 index 0000000..3f68ef5 --- /dev/null +++ b/Chapter12/stlclr_exposedtocs.cpp @@ -0,0 +1,42 @@ +// stlclr_exposedtocs.cpp +// compile with: stlclr_exposedtocs.cpp /clr:pure /LD + +#include + +using namespace cliext; +using namespace System; +using namespace System::Collections::Generic; + +namespace N +{ + +public ref class R +{ + private: + + vector^ m_vec; + + public: + + R(int initial_size) + { + // Create a vector + m_vec = gcnew vector(initial_size); + // Populate it + for (int i = 0; i < initial_size; i++) + { + m_vec[i] = i; + } + } + + // The property to be accessed from C# cannot directly be the STL/CLR + // vector type, + // since that type is assembly-private and cannot be used in a public interface + property IList^ Vec + { + IList^ get() { return m_vec; } + } + +}; + +} diff --git a/Chapter12/stlclr_functor.cpp b/Chapter12/stlclr_functor.cpp new file mode 100644 index 0000000..3f04cba --- /dev/null +++ b/Chapter12/stlclr_functor.cpp @@ -0,0 +1,17 @@ +// stlclr_functor.cpp + +#include + +using namespace cliext; +using namespace System; + +int main() +{ + less func; + + int a = 100; + int b = 200; + + + Console::WriteLine("{0} < {1}? {2}", a, b, func(a, b)); +} diff --git a/Chapter12/stlclr_functor_delegate.cpp b/Chapter12/stlclr_functor_delegate.cpp new file mode 100644 index 0000000..27239a0 --- /dev/null +++ b/Chapter12/stlclr_functor_delegate.cpp @@ -0,0 +1,26 @@ +// stlclr_functor_delegate.cpp + +#include + +using namespace cliext; +using namespace System; + +template +bool IsLessThan(T a, T b) +{ + return a < b; +} + +// managed template delegate type +typedef binary_delegate MyDelegate; + +int main() +{ + MyDelegate^ func = gcnew MyDelegate(&IsLessThan); + + int a = 100; + int b = 200; + + // call managed template delegate + Console::WriteLine("{0} < {1}? {2}", a, b, func(a, b)); +} diff --git a/Chapter12/stlclr_functor_delegate_generic.cpp b/Chapter12/stlclr_functor_delegate_generic.cpp new file mode 100644 index 0000000..1dd7d67 --- /dev/null +++ b/Chapter12/stlclr_functor_delegate_generic.cpp @@ -0,0 +1,23 @@ +// stlclr_functor_delegate_generic.cpp + +#include + +using namespace cliext; +using namespace System; + +int main() +{ + less func; + + // delegate_type is a Microsoft::VisualC::StlClr::BinaryDelegate generic + less::delegate_type^ dlg = func; + + int a = 100; + int b = 200; + + // Call the functor + Console::WriteLine("{0} < {1}? {2}", a, b, func(a, b)); + + // Call the generic delegate + Console::WriteLine("{0} < {1}? {2}", a, b, dlg(a, b)); +} diff --git a/Chapter12/stlclr_iterator.cpp b/Chapter12/stlclr_iterator.cpp new file mode 100644 index 0000000..8223bde --- /dev/null +++ b/Chapter12/stlclr_iterator.cpp @@ -0,0 +1,20 @@ +// stlclr_iterator.cpp + +#include + +using namespace cliext; + +int main() +{ + vector^ vec; + vector::iterator iter; + + vec = gcnew vector(10); + + int count = 0; + for (iter = vec->begin(); iter != vec->end(); iter++) + { + *iter = count++; + printf("%d\n", *iter); + } +} diff --git a/Chapter12/stlclr_make_collection.cpp b/Chapter12/stlclr_make_collection.cpp new file mode 100644 index 0000000..9a32db3 --- /dev/null +++ b/Chapter12/stlclr_make_collection.cpp @@ -0,0 +1,30 @@ +// stlclr_make_collection.cpp + +#include +#include // for make_collection + +using namespace cliext; +using namespace System; +using namespace System::Collections::Generic; + +typedef vector IntVector; +typedef vector::iterator IntVectorIter; + +int main() +{ + + IntVector vec; + for (int i = 0; i < 20; i++) + { + vec.push_back(i); + } + + ICollection^ collection = + make_collection(vec.begin(), vec.end()); + + for each (int i in collection) + { + Console::Write("{0} ", i); + } + Console::WriteLine(); +} diff --git a/Chapter12/stlclr_make_vector.cpp b/Chapter12/stlclr_make_vector.cpp new file mode 100644 index 0000000..4ead79a --- /dev/null +++ b/Chapter12/stlclr_make_vector.cpp @@ -0,0 +1,30 @@ +// stlclr_make_vector.cpp + +#include + +using namespace cliext; +using namespace System; +using namespace System::Collections::Generic; + +typedef vector IntVector; +typedef vector::iterator IntVectorIter; + +int main() +{ + + List^ intList = gcnew List(); + + + for (int i = 0; i < 20; i++) + { + intList->Add(i); + } + + IntVector vec(intList); + + for each (int i in vec) + { + Console::Write("{0} ", i); + } + Console::WriteLine(); +} diff --git a/Chapter12/stlclr_reverse_generic.cpp b/Chapter12/stlclr_reverse_generic.cpp new file mode 100644 index 0000000..91a1a04 --- /dev/null +++ b/Chapter12/stlclr_reverse_generic.cpp @@ -0,0 +1,56 @@ +// stlclr_reverse_generic.cpp + +#include +#include + +using namespace System; +using namespace cliext; + +template +void reverse4(collection_type% coll) +{ + collection_type::iterator iter = coll.begin(); + collection_type::reverse_iterator reverse_iter = coll.rbegin(); + + for (int i = 0; i < coll.size() / 2; ++iter, ++reverse_iter, ++i) + { + collection_type::value_type temp = *reverse_iter; + *reverse_iter = *iter; + *iter = temp; + } +} + +template +void printall(collection_type% coll) +{ + collection_type::iterator iter = coll.begin(); + for (; iter != coll.end(); ++iter) + { + Console::Write("{0} ", *iter); + } + Console::WriteLine(); +} + +int main() +{ + vector v; + for (int i = 0; i < 5; i++) + { + v.push_back(i); + } + + printall(v); + reverse4(v); + printall(v); + + list stringList; + for (int i = 0; i < 5; i++) + { + stringList.push_back( i.ToString()); + } + + printall(stringList); + reverse4(stringList); + printall(stringList); + +} diff --git a/Chapter12/stlclr_reverse_vector.cpp b/Chapter12/stlclr_reverse_vector.cpp new file mode 100644 index 0000000..bd02a50 --- /dev/null +++ b/Chapter12/stlclr_reverse_vector.cpp @@ -0,0 +1,57 @@ +// stlclr_reverse_vector.cpp + +#include +#include + +using namespace System; +using namespace cliext; + +// version 1.0 : vector used as an array +void reverse1(vector% vec) +{ + int n = vec.size(); + for (int i = 0; i < n / 2; i++) + { + int temp = vec[n - i - 1]; + vec[n - i - 1] = vec[i]; + vec[i] = temp; + } +} + +// version 2.0: iterators used +void reverse2(vector% vec) +{ + vector::iterator iter = vec.begin(); + vector::reverse_iterator reverse_iter = vec.rbegin(); + + for (int i = 0; i < vec.size() / 2; ++iter, ++reverse_iter, ++i) + { + int temp = *reverse_iter; + *reverse_iter = *iter; + *iter = temp; + } +} + +void printall(vector% vec) +{ + vector::iterator iter = vec.begin(); + for (; iter != vec.end(); ++iter) + { + Console::Write("{0} ", *iter); + } + Console::WriteLine(); +} + +int main() +{ + vector v; + for (int i = 0; i < 5; i++) + { + v.push_back(i); + } + + printall(v); + reverse1(v); + printall(v); + +} diff --git a/Chapter12/stlclr_vector_assembly1.cpp b/Chapter12/stlclr_vector_assembly1.cpp new file mode 100644 index 0000000..76c79fa --- /dev/null +++ b/Chapter12/stlclr_vector_assembly1.cpp @@ -0,0 +1,22 @@ +// stlclr_vector_assembly1.cpp + +#include + +using namespace cliext; + +typedef vector VectorAssembly1; + +public ref class UsesVector +{ + private: + VectorAssembly1^ m_vec; + + public: + + UsesVector() { m_vec = gcnew VectorAssembly1(); } + + // produces compiler warning since VectorAssembly1 is private + VectorAssembly1^ GetVectorTemplate() { return m_vec; } + + VectorAssembly1::generic_container^ GetVectorGeneric() { return m_vec; } +}; diff --git a/Chapter12/stlclr_vector_assembly2.cpp b/Chapter12/stlclr_vector_assembly2.cpp new file mode 100644 index 0000000..01420fa --- /dev/null +++ b/Chapter12/stlclr_vector_assembly2.cpp @@ -0,0 +1,19 @@ +// stlclr_vector_assembly2.cpp + +#include + +#using "stlclr_vector_assembly1.dll" + +using namespace cliext; + +typedef vector VectorAssembly2; + +int main() +{ + UsesVector^ usesVector = gcnew UsesVector(); + + // Error: candidate function not accessible + VectorAssembly2^ vec1 = usesVector->GetVectorTemplate(); + + VectorAssembly2::generic_container^ vec2 = usesVector->GetVectorGeneric(); +} diff --git a/Chapter12/stlclr_vector_ilist.cpp b/Chapter12/stlclr_vector_ilist.cpp new file mode 100644 index 0000000..d155298 --- /dev/null +++ b/Chapter12/stlclr_vector_ilist.cpp @@ -0,0 +1,31 @@ +// stlclr_vector_ilist.cpp + +#include + +using namespace cliext; +using namespace System; +using namespace System::Collections::Generic; + +int main() +{ + // Create a vector with initial size 10 elements + + vector v(10); + + // Use the vector like an array + + for (int n = 1, i = 0; n < 1024; i++, n *= 2) + { + v[i] = n; + } + + // Retrieve elements using .NET Framework IList interface + + IList^ list = (IList^) %v; + + for each (int i in list) + { + Console::WriteLine("{0} ", i ); + } + +} diff --git a/Chapter12/template_reverse_algorithm.cpp b/Chapter12/template_reverse_algorithm.cpp new file mode 100644 index 0000000..de4b8bc --- /dev/null +++ b/Chapter12/template_reverse_algorithm.cpp @@ -0,0 +1,13 @@ +template +void reverse3(vector% vec) +{ + vector::iterator iter = vec.begin(); + vector::reverse_iterator reverse_iter = vec.rbegin(); + + for (int i = 0; i < vec.size() / 2; ++iter, ++reverse_iter, ++i) + { + int temp = *reverse_iter; + *reverse_iter = *iter; + *iter = temp; + } +} diff --git a/Chapter13/Program.cs b/Chapter13/Program.cs new file mode 100644 index 0000000..67e78b7 --- /dev/null +++ b/Chapter13/Program.cs @@ -0,0 +1,15 @@ +// Program.cs +using System; +using System.Collections.Generic; +using System.Text; + +class Program +{ + static void Main(string[] args) + { + MessageBoxWrapper wrapper = + new MessageBoxWrapper("I hope you love this message box!", + "C# using Native Message Box", MessageBoxTypeEnum.OKCANCEL); + wrapper.Display(); + } +} diff --git a/Chapter13/auto_gcroot.cpp b/Chapter13/auto_gcroot.cpp new file mode 100644 index 0000000..703a20e --- /dev/null +++ b/Chapter13/auto_gcroot.cpp @@ -0,0 +1,44 @@ +// auto_gcroot.cpp + +#include +#include +using namespace System; +using namespace msclr; + +ref class R +{ + public: + void f() + { + Console::WriteLine("managed member function"); + } + + ~R() + { + Console::WriteLine("destructor"); + } + +}; + +class N +{ + gcroot r_gcroot; + auto_gcroot r_auto_gcroot; + + public: + N() + { + r_gcroot = gcnew R(); + r_gcroot->f(); + r_auto_gcroot = gcnew R(); + r_auto_gcroot->f(); + } + +}; + +int main() +{ + N n; + // when n gets destroyed, the destructor for the auto_gcroot object + // will be executed, but not the gcroot object +} diff --git a/Chapter13/consume_cpp.cs b/Chapter13/consume_cpp.cs new file mode 100644 index 0000000..09de981 --- /dev/null +++ b/Chapter13/consume_cpp.cs @@ -0,0 +1,13 @@ +// consume_cpp.cs +// compile with: csc /r:global_function.dll consume_cpp.cs + +using G; + +class C +{ + public static void Main() + { + // FGlobal(); // error: global functions not available in C# + R.FMember(); // OK + } +}; diff --git a/Chapter13/context_switch.cpp b/Chapter13/context_switch.cpp new file mode 100644 index 0000000..67c734f --- /dev/null +++ b/Chapter13/context_switch.cpp @@ -0,0 +1,44 @@ +// context_switch.cpp +#include +#include +#include +#include +#include + +#pragma unmanaged +int native_function(wchar_t* str1, wchar_t* str2 ) +{ + int i = 0; + while (*str1++ = *str2++) i++; + return i; +} + +#pragma managed + +wchar_t* random_string(wchar_t* wcs, int n) +{ + for (int i = 0; i < n - 1; i++) + { + wcs[i] = (wchar_t) floor(((double) rand() / (double) RAND_MAX * 26)) + L'A'; + } + return wcs; +} +// try commenting out the pragma above random_string and uncomment this: +// #pragma managed + +int main() +{ + wchar_t wcs1[100]; + wchar_t* wcs2 = new wchar_t[100]; + memset(wcs1, 0, 100 * sizeof(wchar_t)); + clock_t t = clock(); + const int num_iter = 100000; + for (int i = 0; i < num_iter; i++) + { + random_string(wcs1, 100); + native_function(wcs2, wcs1); + } + double time_elapsed = (clock()-t)/(double)CLOCKS_PER_SEC; + printf("total time elapsed: %2.2f seconds\n", time_elapsed); +} + diff --git a/Chapter13/gc_hole.cpp b/Chapter13/gc_hole.cpp new file mode 100644 index 0000000..60260fe --- /dev/null +++ b/Chapter13/gc_hole.cpp @@ -0,0 +1,32 @@ +// gc_hole.cpp +using namespace System; + +ref struct R +{ + array^ a; + + R() + { + a = gcnew array { 1, 2, 3, 4, 5 }; + } +}; + +void F(int* ptr) +{ + if (ptr) + Console::WriteLine(*ptr); // possible crash +} + +int* GcHole(R^ r) // gc hole +{ + pin_ptr pinp = &r->a[0]; + int *ptr; + ptr = pinp; // pointer assigned to pinning pointer + // ... + return ptr; // pointer into gc heap returned (!) +} + +int main() { + R^ r = gcnew R; + F(GcHole(r)); +} diff --git a/Chapter13/getlasterror.cpp b/Chapter13/getlasterror.cpp new file mode 100644 index 0000000..a11b36e --- /dev/null +++ b/Chapter13/getlasterror.cpp @@ -0,0 +1,36 @@ +// getlasterror.cpp + +#using "System.dll" + +using namespace System; +using namespace System::ComponentModel; // for Win32Exception +using namespace System::Runtime::InteropServices; + +[DllImport("kernel32.dll", SetLastError=true)] +extern bool SetVolumeLabel(String^ lpRootPathName, String^ lpVolumeName); + +bool TestGetLastWin32Error() +{ + if (SetVolumeLabel("BAD:\\", "VolumeName")) + { + System::Console::WriteLine("Success!"); + return true; + } + else + { + throw gcnew Win32Exception(Marshal::GetLastWin32Error()); + } + return false; +} + +int main() +{ + try + { + TestGetLastWin32Error(); + } + catch(Win32Exception^ e) + { + Console::WriteLine(e->ToString()); + } +} diff --git a/Chapter13/global_function.cpp b/Chapter13/global_function.cpp new file mode 100644 index 0000000..a617843 --- /dev/null +++ b/Chapter13/global_function.cpp @@ -0,0 +1,23 @@ +// global_function.cpp +// compile with: cl /clr:safe /LD global_function.cpp + +using namespace System; + +namespace G +{ + + void FGlobal() + { + Console::WriteLine("Global C++/CLI Function."); + } + + public ref class R + { + public: + static void FMember() + { + Console::WriteLine("C++/CLI Static Member Function."); + FGlobal(); + } + }; +}; diff --git a/Chapter13/implement_example.vb b/Chapter13/implement_example.vb new file mode 100644 index 0000000..c1f5e31 --- /dev/null +++ b/Chapter13/implement_example.vb @@ -0,0 +1,22 @@ +' implement_example.vb +' compile with: vbc /r:interface_example.dll implement_example.vb + +Public Class VBClass + Implements ITest + + Public Sub F Implements ITest.F + Console.WriteLine("F in VB") + End Sub + + Public Sub G Implements ITest.G + Console.WriteLine("G in VB") + End Sub + + Public Shared Sub Main + Dim Test As ITest = New VBClass + With Test + .F() + .G() + End With + End Sub 'Main +End Class 'VBClass diff --git a/Chapter13/interface_example.cpp b/Chapter13/interface_example.cpp new file mode 100644 index 0000000..96c8b72 --- /dev/null +++ b/Chapter13/interface_example.cpp @@ -0,0 +1,8 @@ +// interface_example.cpp +// compile with: cl /clr:pure /LD interface_example.cpp + +public interface class ITest +{ + void F(); + void G(); +}; diff --git a/Chapter13/interior_ptr.cpp b/Chapter13/interior_ptr.cpp new file mode 100644 index 0000000..eabf274 --- /dev/null +++ b/Chapter13/interior_ptr.cpp @@ -0,0 +1,23 @@ +// interior_ptr.cpp +using namespace System; + +ref struct S +{ + array^ array1; + + S() + { + array1 = gcnew array(10) + { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90 }; + } + + void f() + { + interior_ptr p = &array1[0]; + for (int i = 0; i < 10; i++) + { + Console::WriteLine(*p++); + } + } +}; + diff --git a/Chapter13/interop_messagebox.cpp b/Chapter13/interop_messagebox.cpp new file mode 100644 index 0000000..1541ecb --- /dev/null +++ b/Chapter13/interop_messagebox.cpp @@ -0,0 +1,35 @@ +// interop_messagebox.cpp + +#include +//#include // for PtrToStringChars +#include + +using namespace System; +using namespace msclr::interop; + +public ref class MessageBoxClass +{ + public: + + property String^ Message; + property String^ Caption; + + int DisplayBox() + { + marshal_context context; + /*// Use pinning pointers to lock down the data while it's being + // used in native code. + pin_ptr message = PtrToStringChars(Message); + pin_ptr caption = PtrToStringChars(Caption);*/ + return MessageBoxW( 0, context.marshal_as(Message), + context.marshal_as(Caption), MB_OK); + } +}; + +int main() +{ + MessageBoxClass m; + m.Message = "Managed string used in native function"; + m.Caption = "Managed Code using Win32 Message Box"; + m.DisplayBox(); +} diff --git a/Chapter13/interop_messagebox_marshaling.cpp b/Chapter13/interop_messagebox_marshaling.cpp new file mode 100644 index 0000000..5988df9 --- /dev/null +++ b/Chapter13/interop_messagebox_marshaling.cpp @@ -0,0 +1,31 @@ +// interop_messagebox_marshaling.cpp + +#include + +#include + +using namespace System; +using namespace msclr::interop; + +public ref class MessageBoxClass +{ + public: + + property String^ Message; + property String^ Caption; + + int DisplayBox() + { + marshal_context context; + return MessageBoxW( 0, context.marshal_as(Message), + context.marshal_as(Caption), MB_OK); + } +}; + +int main() +{ + MessageBoxClass m; + m.Message = "Managed string used in native function"; + m.Caption = "Managed Code using Win32 Message Box"; + m.DisplayBox(); +} diff --git a/Chapter13/message_box.cpp b/Chapter13/message_box.cpp new file mode 100644 index 0000000..dbfa589 --- /dev/null +++ b/Chapter13/message_box.cpp @@ -0,0 +1,8 @@ +// message_box.cpp + +#include + +int main() +{ + MessageBox( 0, "Hello, World!", "Win32 Message Box", 0); +} diff --git a/Chapter13/message_box_wrapper.cpp b/Chapter13/message_box_wrapper.cpp new file mode 100644 index 0000000..1088170 --- /dev/null +++ b/Chapter13/message_box_wrapper.cpp @@ -0,0 +1,109 @@ +// message_box_wrapper.cpp + +#include "native_message_box_class.h" +#include + +using namespace System; + +enum class MessageBoxTypeEnum +{ + OK, OKCANCEL, ABORTRETRYIGNORE, + YESNOCANCEL, YESNO, + RETRYCANCEL, CANCELTRYCONTINUE, + ICONHAND = 0x10, + ICONQUESTION = 0x20, + ICONEXCLAMATION = 0x30, + ICONASTERISK = 0x40, + TYPEMASK = 0xF, + ICONMASK = 0xF0 +}; + +wchar_t* MarshalString(String^ s, size_t sizeInCharacters) +{ + pin_ptr pinnedChars = PtrToStringChars(s); + wchar_t* wcs = new wchar_t[sizeInCharacters]; + wcscpy_s(wcs, sizeInCharacters, pinnedChars); + return wcs; +} + +public ref class MessageBoxWrapper +{ + + MessageBoxClass* nativeMessageBox; + literal unsigned int maxSize = 1024; + + public: + + MessageBoxWrapper(String^ message, String^ caption, MessageBoxTypeEnum type) + { + pin_ptr pinnedMessage = PtrToStringChars(message); + pin_ptr pinnedCaption = PtrToStringChars(caption); + + nativeMessageBox = new MessageBoxClass( + pinnedMessage, pinnedCaption, + static_cast(type)); + } + + property String^ Caption + { + String^ get() + { + return gcnew String(nativeMessageBox->GetCaption()); + } + void set(String^ s) + { + nativeMessageBox->SetCaption( MarshalString(s, maxSize) ); + } + } + property String^ Message + { + String^ get() + { + return gcnew String(nativeMessageBox->GetCaption()); + } + void set(String^ s) + { + nativeMessageBox->SetMessage( MarshalString(s, maxSize) ); + } + } + property MessageBoxTypeEnum Type + { + MessageBoxTypeEnum get() + { + return static_cast(nativeMessageBox->GetType()); + } + void set(MessageBoxTypeEnum t) + { + nativeMessageBox->SetType( static_cast( t )); + } + } + int Display() + { + if (nativeMessageBox != NULL) + return nativeMessageBox->Display(); + else return -1; + } + + ~MessageBoxWrapper() + { + this->!MessageBoxWrapper(); + } + + !MessageBoxWrapper() + { + delete nativeMessageBox; + + } + +}; + +int main() +{ + MessageBoxWrapper^ wrapper = gcnew MessageBoxWrapper( + "Do you like this message box?", + "Managed wrapper message box.", + MessageBoxTypeEnum::YESNO); + Console::WriteLine("Message is: {0}", wrapper->Message); + int result = wrapper->Display(); + Console::WriteLine("Result was {0}", result); +} diff --git a/Chapter13/message_box_wrapper_marshaling.cpp b/Chapter13/message_box_wrapper_marshaling.cpp new file mode 100644 index 0000000..9e471b8 --- /dev/null +++ b/Chapter13/message_box_wrapper_marshaling.cpp @@ -0,0 +1,104 @@ +// message_box_wrapper_marshaling.cpp + +#include "native_message_box_class.h" + +#include + +using namespace System; +using namespace msclr::interop; + +enum class MessageBoxTypeEnum +{ + OK, OKCANCEL, ABORTRETRYIGNORE, + YESNOCANCEL, YESNO, + RETRYCANCEL, CANCELTRYCONTINUE, + ICONHAND = 0x10, + ICONQUESTION = 0x20, + ICONEXCLAMATION = 0x30, + ICONASTERISK = 0x40, + TYPEMASK = 0xF, + ICONMASK = 0xF0 +}; + +public ref class MessageBoxWrapper +{ + + MessageBoxClass* nativeMessageBox; + literal unsigned int maxSize = 1024; + + public: + + MessageBoxWrapper(String^ message, String^ caption, MessageBoxTypeEnum type) + { + marshal_context context; + nativeMessageBox = new MessageBoxClass( + context.marshal_as(message), + context.marshal_as(caption), + static_cast(type)); + } + + property String^ Caption + { + String^ get() + { + return marshal_as(nativeMessageBox->GetCaption()); + } + void set(String^ s) + { + marshal_context context; + nativeMessageBox->SetCaption( context.marshal_as(s) ); + } + } + property String^ Message + { + String^ get() + { + return marshal_as(nativeMessageBox->GetCaption()); + } + void set(String^ s) + { + marshal_context context; + nativeMessageBox->SetMessage( context.marshal_as(s) ); + } + } + property MessageBoxTypeEnum Type + { + MessageBoxTypeEnum get() + { + return static_cast(nativeMessageBox->GetType()); + } + void set(MessageBoxTypeEnum t) + { + nativeMessageBox->SetType( static_cast( t )); + } + } + int Display() + { + if (nativeMessageBox != NULL) + return nativeMessageBox->Display(); + else return -1; + } + + ~MessageBoxWrapper() + { + this->!MessageBoxWrapper(); + } + + !MessageBoxWrapper() + { + delete nativeMessageBox; + + } + +}; + +int main() +{ + MessageBoxWrapper^ wrapper = gcnew MessageBoxWrapper( + "Do you like this message box?", + "Managed wrapper message box.", + MessageBoxTypeEnum::YESNO); + Console::WriteLine("Message is: {0}", wrapper->Message); + int result = wrapper->Display(); + Console::WriteLine("Result was {0}", result); +} diff --git a/Chapter13/native_exception.cpp b/Chapter13/native_exception.cpp new file mode 100644 index 0000000..4649604 --- /dev/null +++ b/Chapter13/native_exception.cpp @@ -0,0 +1,69 @@ +// native_exception.cpp +#include + +using namespace System; +using namespace System::Runtime::InteropServices; + +#pragma unmanaged + +class NativeException +{ + wchar_t m_str[1024]; + + public: + + NativeException(wchar_t* s) + { + wcscpy_s(m_str, s); + } + + const wchar_t* GetMessage() { return m_str; } +}; + +void throw_native_exception(bool byval) +{ + if (byval) + throw NativeException(L"Native Exception By Value"); + else + throw new NativeException(L"Native Exception on Native Heap"); +} + +#pragma managed + +int main() +{ + bool byval = true; + + try + { + throw_native_exception(byval); + } + catch(NativeException& native_exception) + { + wprintf(L"Caught NativeException: %s\n", native_exception.GetMessage()); + } + catch(SEHException^ e) + { + Console::WriteLine("{0}\nErrorCode: 0x{1:x}", + e->ToString(), e->ErrorCode); + } + + byval = false; + + try + { + throw_native_exception(byval); + } + catch(NativeException* native_exception) + { + wprintf(L"Caught NativeException: %s\n", native_exception->GetMessage()); + } + catch(SEHException^ e) + { + Console::WriteLine("{0}\nErrorCode: 0x{1:x}", + e->ToString(), e->ErrorCode); + } +} + + + diff --git a/Chapter13/native_in_managed.cpp b/Chapter13/native_in_managed.cpp new file mode 100644 index 0000000..bf1e0b4 --- /dev/null +++ b/Chapter13/native_in_managed.cpp @@ -0,0 +1,137 @@ +// native_in_managed2.cpp + +#include +#include +#include +#include + +using namespace System; +using namespace System::Runtime::InteropServices; +using namespace msclr::interop; + +// template for embedding a native class +// in a reference type +template +ref class native_root +{ + T* t; + + !native_root() + { + if (t) + { + delete t; + t = NULL; + } + } + + ~native_root() + { + this->!native_root(); + } + + public: + + native_root() : t(new T) {} + + // these must be static to prevent them from being used + // within the class (e.g. when we use this-> in ~native_root) + + // allows access to the underlying pointer + static T* operator&(native_root% n) { return n.t; } + // allows -> to be used to access members + static T* operator->(native_root% n) { return n.t; } +}; + +class native_exception {}; + +// typical native class +class NativeClass +{ + FILE* fp; + static const int TIME_BUFFER_SIZE = 32; + + public: + NativeClass() + { + printf("Opening the file.\n"); + // open a file for Unicode writing. + int errcode = fopen_s(&fp, "myfile.txt", "a+, ccs=UNICODE"); + if (errcode != 0) + { + throw new native_exception; + } + } + + void OutputText(const wchar_t* text) + { + if (fp) + { + wprintf(text); + fwprintf(fp, text); + } + else + { + throw new native_exception; + } + } + + void TimeStamp() + { + tm newtime; + __time32_t time; + wchar_t time_text[TIME_BUFFER_SIZE]; + _time32( &time ); + _localtime32_s( &newtime, &time ); + _wasctime_s(time_text, TIME_BUFFER_SIZE, &newtime); + if (fp) + { + wprintf(time_text); + fwprintf(fp, time_text); + } + else + { + throw new native_exception; + } + } + + ~NativeClass() + { + printf("Closing the file.\n"); + if (fp) + { + fclose(fp); + } + } +}; + +// A reference type enclosing a Native Class +ref class R +{ + native_root n; + + public: + + R() { } + + // marshal the String to a Unicode string + // and pass the pointer to the native class method + void OutputToFile(String^ s) + { + marshal_context context; + n->OutputText(context.marshal_as(s)); + n->TimeStamp(); + } +}; + +int main() +{ + R^ r1 = gcnew R(); + r1->OutputToFile("Output through native class!\n"); + delete r1; // the file is closed + + R r2; + r2.OutputToFile("More output\n"); + // file is closed again when r2 is cleaned up +} + diff --git a/Chapter13/native_in_managed_bad.cpp b/Chapter13/native_in_managed_bad.cpp new file mode 100644 index 0000000..2eb7b6e --- /dev/null +++ b/Chapter13/native_in_managed_bad.cpp @@ -0,0 +1,17 @@ +// native_in_managed.cpp + +using namespace System; + +ref class R {}; + +class N +{ + R^ r; // illegal + + public: + N() + { + r = gcnew R(); + } + +}; diff --git a/Chapter13/native_message_box_class.cpp b/Chapter13/native_message_box_class.cpp new file mode 100644 index 0000000..ecb41ac --- /dev/null +++ b/Chapter13/native_message_box_class.cpp @@ -0,0 +1,18 @@ +// native_message_box.cpp +#include "native_message_box_class.h" + +int main() +{ + MessageBoxClass* messageBox = new MessageBoxClass( + L"Do you like this example?", L"Native message box", + static_cast(YESNOCANCEL | ICONASTERISK)); + + int result = messageBox->Display(); + + wchar_t wstr[1024]; + swprintf_s( wstr, L"The dialog result was %d", result); + messageBox->SetMessage(wstr); + messageBox->SetType(OK); + messageBox->Display(); + +} diff --git a/Chapter13/native_message_box_class.h b/Chapter13/native_message_box_class.h new file mode 100644 index 0000000..f7978cc --- /dev/null +++ b/Chapter13/native_message_box_class.h @@ -0,0 +1,73 @@ +// native_message_box_class.h + +#include +#include + +enum MessageBoxType +{ + OK, OKCANCEL, ABORTRETRYIGNORE, + YESNOCANCEL, YESNO, + RETRYCANCEL, CANCELTRYCONTINUE, + ICONHAND = 0x10, + ICONQUESTION = 0x20, + ICONEXCLAMATION = 0x30, + ICONASTERISK = 0x40, + TYPEMASK = 0xF, + ICONMASK = 0xF0 +}; + +class MessageBoxClass +{ + + wchar_t* m_message; + wchar_t* m_caption; + MessageBoxType m_type; + static const size_t sz = 1024; + + public: + + MessageBoxClass(const wchar_t* message, const wchar_t* caption, + MessageBoxType type) + : m_type(type) + { + m_message = new wchar_t[sz]; + m_caption = new wchar_t[sz]; + wcscpy_s(m_message, sz, message); // using the "safe" CRT + wcscpy_s(m_caption, sz, caption); + } + + void SetMessage(const wchar_t* message) + { + if (message != NULL) + { + wcscpy_s(m_message, sz, message); + } + } + const wchar_t* GetMessage() const { return m_message; } + + void SetCaption(const wchar_t* caption) + { + if (caption != NULL) + { + wcscpy_s(m_caption, sz, caption); + } + } + const wchar_t* GetCaption() const { return m_caption; } + + MessageBoxType GetType() const { return m_type; } + void SetType(MessageBoxType type){ m_type = type; } + + + + int Display() + { + return MessageBoxW(0, m_message, m_caption, m_type); + } + + ~MessageBoxClass() + { + delete m_message; + delete m_caption; + } + +}; diff --git a/Chapter13/nativeclasslib.cpp b/Chapter13/nativeclasslib.cpp new file mode 100644 index 0000000..7f13d33 --- /dev/null +++ b/Chapter13/nativeclasslib.cpp @@ -0,0 +1,59 @@ +// nativeclasslib.cpp +// compile with: cl /LD nativeclasslib.cpp + +#include + +class __declspec(dllexport) NativeClass +{ + private: + int m_member; + public: + NativeClass() : m_member(1) { } + + int F( int i ) + { + // __FUNCSIG__ is a compiler-defined macro evaluating + // to the current function signature + printf("%s\n", __FUNCSIG__); + return m_member + i; + } + + static NativeClass* CreateObject() + { + printf("%s\n", __FUNCSIG__); + return new NativeClass(); + } + + static void DeleteObject(NativeClass* p) + { + printf("%s\n", __FUNCSIG__); + delete p; + } + }; + +// If you do not want to use the obfuscated names, you can use these exports: + +extern "C" __declspec(dllexport) NativeClass* CreateObject() +{ + return NativeClass::CreateObject(); +} + +extern "C" __declspec(dllexport) void DeleteObject(NativeClass* p) +{ + NativeClass::DeleteObject(p); +} + +/* The mangled names were obtained by running the command + link /DUMP /EXPORTS nativeclasslib.dll + which outputs: + + ordinal hint RVA name + + 1 0 00001000 ??0NativeClass@@QAE@XZ + 2 1 000010D0 ??4NativeClass@@QAEAAV0@ABV0@@Z + 3 2 00001050 ?CreateObject@NativeClass@@SAPAV1@XZ + 4 3 000010A0 ?DeleteObject@NativeClass@@SAXPAV1@@Z + 5 4 00001020 ?F@NativeClass@@QAEHH@Z + 6 5 000010F0 CreateObject + 7 6 00001100 DeleteObject +*/ diff --git a/Chapter13/nativeclasslib.exp b/Chapter13/nativeclasslib.exp new file mode 100644 index 0000000..47254c2 Binary files /dev/null and b/Chapter13/nativeclasslib.exp differ diff --git a/Chapter13/nativeclasslib.lib b/Chapter13/nativeclasslib.lib new file mode 100644 index 0000000..27cd2e9 Binary files /dev/null and b/Chapter13/nativeclasslib.lib differ diff --git a/Chapter13/pinvoke.cpp b/Chapter13/pinvoke.cpp new file mode 100644 index 0000000..b5345a9 --- /dev/null +++ b/Chapter13/pinvoke.cpp @@ -0,0 +1,13 @@ +// pinvoke.cpp +using namespace System; +using namespace System::Runtime::InteropServices; + +// note the use of managed equivalents of native types +[DllImport("user32.dll", CharSet=CharSet::Auto)] +int MessageBox(IntPtr, String^ text, String^ caption, + unsigned int type); + +int main() +{ + MessageBox(IntPtr::Zero, "Hello, World!", "Win32 Message Box", 0); +} diff --git a/Chapter13/pinvoke_marshaling.cpp b/Chapter13/pinvoke_marshaling.cpp new file mode 100644 index 0000000..0237480 --- /dev/null +++ b/Chapter13/pinvoke_marshaling.cpp @@ -0,0 +1,20 @@ +// pinvoke_marshaling.cpp + +#include +using namespace System; +using namespace System::Runtime::InteropServices; +using namespace msclr::interop; + +// note the use of managed equivalents of native types +[DllImport("user32.dll", CharSet=CharSet::Auto)] +int MessageBox(HANDLE, const wchar_t*, const wchar_t*, + unsigned int type); + +int main() +{ + String^ message = "Hello World!"; + String^ caption = "Win32 Message Box"; + marshal_context context; + MessageBox(marshal_as(IntPtr::Zero), context.marshal_as(message), + context.marshal_as(caption), 0); +} diff --git a/Chapter13/pinvoke_rename_entry_point.cpp b/Chapter13/pinvoke_rename_entry_point.cpp new file mode 100644 index 0000000..279ab16 --- /dev/null +++ b/Chapter13/pinvoke_rename_entry_point.cpp @@ -0,0 +1,17 @@ +// pinvoke_rename_entry_point.cpp + +#using "System.Windows.Forms.dll" + +using namespace System; +using namespace System::Runtime::InteropServices; +using namespace System::Windows::Forms; + +[DllImport("user32.dll", CharSet=CharSet::Auto, EntryPoint="MessageBox")] +int NativeMessageBox(IntPtr, String^ text, String^ caption, + unsigned int type); + +int main() +{ + NativeMessageBox(IntPtr::Zero, "Hello, World!", "Win32 Message Box", 0); + MessageBox::Show("Hello, Universe!", "Managed Message Box"); +} diff --git a/Chapter13/pinvoke_thiscall.cpp b/Chapter13/pinvoke_thiscall.cpp new file mode 100644 index 0000000..b010fda --- /dev/null +++ b/Chapter13/pinvoke_thiscall.cpp @@ -0,0 +1,33 @@ +// pinvoke_thiscall.cpp +// compile with: cl /clr:safe pinvoke_thiscall.cpp + +using namespace System; +using namespace System::Text; +using namespace System::Runtime::InteropServices; + +namespace NativeLib +{ + [ DllImport( "nativeclasslib.dll", + EntryPoint="?F@NativeClass@@QAEHH@Z", + CallingConvention=CallingConvention::ThisCall )] + extern int F( IntPtr ths, int i ); + + // static NativeClass* NativeClass::CreateObject(); + [DllImport( "nativeclasslib.dll", EntryPoint= + "?CreateObject@NativeClass@@SAPAV1@XZ" )] + extern IntPtr CreateObject(); + + // static void NativeClass::DeleteClass( NativeClass* p ) + [ DllImport( "nativeclasslib.dll", EntryPoint= + "?DeleteObject@NativeClass@@SAXPAV1@@Z" )] + extern void DeleteObject( IntPtr p ); +} + + +int main() +{ + IntPtr ptr = NativeLib::CreateObject(); + int result = NativeLib::F( ptr, 50 ); + Console::WriteLine( "Return value: {0} ", result ); + NativeLib::DeleteObject( ptr ); +} diff --git a/Chapter13/try_except.cpp b/Chapter13/try_except.cpp new file mode 100644 index 0000000..984a45c --- /dev/null +++ b/Chapter13/try_except.cpp @@ -0,0 +1,93 @@ +// try_except.cpp +#include +#include // for EXCEPTION_INT_DIVIDE_BY_ZERO +#include + +using namespace System; +using namespace System::Runtime::InteropServices; + +#pragma unmanaged +void generate_SEH_exception() +{ + int i = 0; + // divide by zero generates an SEH exception + int x = 2 / i; +} + +void generate_AV() +{ + int *pn = 0; + int n = *pn; // generates an Access Violation +} + +int filter_div0(unsigned int code, struct _EXCEPTION_POINTERS *ep) +{ + + if (code == EXCEPTION_INT_DIVIDE_BY_ZERO) + { + return EXCEPTION_EXECUTE_HANDLER; + } + else + { + return EXCEPTION_CONTINUE_SEARCH; + }; +} + +// This must be a native function because __try/__except is not +// allowed in the same function as code that uses try/catch +void try_except(bool bThrowUnhandledAV) +{ + __try + { + if (bThrowUnhandledAV) + generate_AV(); + else + generate_SEH_exception(); + } + __except( filter_div0(GetExceptionCode(), GetExceptionInformation())) + { + printf_s("Divide by zero exception caught via SEH __except block."); + } +} + +#pragma managed + +int main(array^ args) +{ + if (args->Length < 1) + { + Console::WriteLine("Usage: try_except [NET|SEH|AV]"); + return -1; + } + if (args[0] == "NET") // Demonstrate catching SEH as a .NET Exception + { + try + { + generate_SEH_exception(); + } + catch(DivideByZeroException^ e) + { + Console::WriteLine(e->ToString()); + } + } + else if (args[0] == "SEH") // Demonstrate handling SEH exception natively + { + // call native function with try/except block + // and filters out division by zero exceptions + try_except(false); + } + else if (args[0] == "AV") // Demonstrate filtering of what exceptions to handle + // natively and what to allow through + { + try + { + // AV's, however, are not filtered and are allowed + // to propagate to managed code + try_except(true); + } + catch(AccessViolationException^ e) + { + Console::WriteLine(e->ToString()); + } + } +} diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..a79d649 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,27 @@ +Freeware License, some rights reserved + +Copyright (c) 2008 Gordon Hogenson + +Permission is hereby granted, free of charge, to anyone obtaining a copy +of this software and associated documentation files (the "Software"), +to work with the Software within the limits of freeware distribution and fair use. +This includes the rights to use, copy, and modify the Software for personal use. +Users are also allowed and encouraged to submit corrections and modifications +to the Software for the benefit of other users. + +It is not allowed to reuse, modify, or redistribute the Software for +commercial use in any way, or for a user’s educational materials such as books +or blog articles without prior permission from the copyright holder. + +The above copyright notice and this permission notice need to be included +in all copies or substantial portions of the software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS OR APRESS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..86e2fae --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +#Apress Source Code + +This repository accompanies [*Foundations of C++/CLI*](http://www.apress.com/9781430210238) by Gordon Hogenson (Apress, 2008). + +![Cover image](9781430210238.jpg) + +Download the files as a zip using the green button, or clone the repository to your machine using Git. + +##Releases + +Release v1.0 corresponds to the code in the published book, without corrections or updates. + +##Contributions + +See the file Contributing.md for more information on how you can contribute to this repository. diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..f628f1c --- /dev/null +++ b/README.txt @@ -0,0 +1,27 @@ +The code in this folder supports the book "Foundations of C++/CLI: The Visual C++ Language for .NET" +(Apress, 2008). + +The code is intended to compile with Visual Studio 2008 and .NET 3.5. In most cases, the following +command line will compile each .cpp file: + +cl /clr filename.cpp + +Most of the code will also compile with other variations of the /clr compiler option, including /clr:pure +and /clr:safe. For details, see the text. + +If the compilation command line differs from the above, the correct command line is given in the code. For +example, many samples produce a DLL instead of an EXE, in which case the /LD command line option is used. + +Not all numbered code snippets in the book appear in this set of files. If a code snippet in the book is +not included here, it may be that the code snippet is part of another snippet, or in a few cases, the code +may be from a library or system header file. + +Executing the code that results from the compilation should produce the output in the book. + +This code is intended to be executed and compiled on a single development machine. Executing code on +a different machine than the machine where you compiled it is referred to as deployment. These rules +are described in the Visual C++ documentation. + + + + \ No newline at end of file diff --git a/contributing.md b/contributing.md new file mode 100644 index 0000000..f6005ad --- /dev/null +++ b/contributing.md @@ -0,0 +1,14 @@ +# Contributing to Apress Source Code + +Copyright for Apress source code belongs to the author(s). However, under fair use you are encouraged to fork and contribute minor corrections and updates for the benefit of the author(s) and other readers. + +## How to Contribute + +1. Make sure you have a GitHub account. +2. Fork the repository for the relevant book. +3. Create a new branch on which to make your change, e.g. +`git checkout -b my_code_contribution` +4. Commit your change. Include a commit message describing the correction. Please note that if your commit message is not clear, the correction will not be accepted. +5. Submit a pull request. + +Thank you for your contribution! \ No newline at end of file