Click here to Skip to main content
15,895,746 members
Articles / Database Development / SQL Server

CLinq - LINQ support for the C++/CLI language

Rate me:
Please Sign up or sign in to vote.
4.86/5 (22 votes)
27 Jul 2007Ms-PL19 min read 64.3K   1.1K   37  
CLinq project is a library that makes it possible to use LINQ technologies from the C++/CLI language
// EeekSoft.CLinq.h

#pragma once

#include "EeekSoft.CLinq.Utils.h"

using namespace System;
using namespace System::Expressions;
using namespace System::Reflection;
using namespace System::Collections::Generic;

namespace EeekSoft {
	namespace CLinq {
		/// <summary>
		/// Abstract untyped expression
		/// </summary>
		public ref class GenericExpression abstract
		{
		public:
			/// <summary> 
			/// Returns LINQ representation of the expression. 
			/// </summary>
			virtual System::Expressions::Expression^ ToLinq() abstract;
		};

		/// <summary>
		/// Abstract typed expression
		/// </summary>
		generic<typename T> public ref class Expression abstract : GenericExpression 
		{
		protected:
			/// <summary> Invoke method by name. Returns expression representing method call. </summary>
			generic<typename R> Expression<R>^ ExpressionInvoke(String^ name, ... cli::array<GenericExpression^>^ args);
			/// <summary> Invoke static method by name. Returns expression representing method call. </summary>
			generic<typename R> static Expression<R>^ ExpressionInvokeStatic(String^ name, ... cli::array<GenericExpression^>^ args);
			/// <summary> Read property by name. Returns expression representing property reading. </summary>
			generic<typename R> Expression<R>^ ExpressionProp(String^ name);
			/// <summary> Read property by name. Returns expression representing property reading. </summary>
			generic<typename R> static Expression<R>^ ExpressionPropStatic(String^ name);

		public:
			operator Expression<T>^() { return this; }
		};

		/// <summary>
		/// Represents conversion from untyped to typed expression
		/// </summary>
		generic<typename T> public ref class TypedExpression : Expression<T>
		{
		protected:
			GenericExpression^ _inner;
		public:
			TypedExpression() { }

			/// <summary>
			/// Wraps specified generic expression. 
			/// </summary>
			TypedExpression(GenericExpression^ expr)
			{
				_inner = expr;
			}

			/// <summary> 
			/// Returns LINQ representation of the expression. 
			/// </summary>
			virtual System::Expressions::Expression^ ToLinq() override 
			{
				return _inner->ToLinq();
			}
		};

		/// <summary>
		/// Represents conversion from expression of another type to specified type
		/// </summary>
		generic<typename T> public ref class ConvertExpression : Expression<T>
		{
			GenericExpression^ _expr;
		public:
			ConvertExpression(GenericExpression^ expr)
			{
				_expr = expr;
			}

			/// <summary> 
			/// Returns LINQ representation of the expression. 
			/// </summary>
			virtual System::Expressions::Expression^ ToLinq() override 
			{
				return System::Expressions::Expression::Convert(_expr->ToLinq(), T::typeid);
			}
		};

		/// <summary>
		/// Represents creating of array that will be passed as an params parameter
		/// </summary>
		generic<typename T> public ref class ParamsExpression : GenericExpression
		{
		private:
			System::Expressions::Expression^ _value;

		public:
			ParamsExpression(... cli::array<Expression<T>^>^ arr)
			{
				List<System::Expressions::Expression^>^ ret = gcnew List<System::Expressions::Expression^>();
				for(int i=0; i<arr->Length; i++) ret->Add(arr[i]->ToLinq());
				_value = System::Expressions::Expression::NewArrayInit(T::typeid, ret);
			}

			/// <summary> 
			/// Returns LINQ representation of the expression. 
			/// </summary>
			virtual System::Expressions::Expression^ ToLinq() override 
			{
				return _value;
			}
		};


		/// <summary>
		/// Represent reading of a property with unknown return type
		/// </summary>
		public ref class PropertyExpressionUntyped : GenericExpression
		{
		private:
			PropertyInfo^ _prop;
			GenericExpression^ _instance;
			
		public:
			PropertyExpressionUntyped(PropertyInfo^ prop, GenericExpression^ instance)
			{
				_prop = prop;
				_instance = instance;
			}

			/// <summary> 
			/// Returns LINQ representation of the expression. 
			/// </summary>
			virtual System::Expressions::Expression^ ToLinq() override 
			{
				System::Expressions::Expression^ self = nullptr;
				if (_instance) self = _instance->ToLinq();
				return System::Expressions::Expression::Property(self, _prop);
			}
		};

		/// <summary>
		/// Represent reading of a property with known return type
		/// </summary>
		generic<typename T> public ref class PropertyExpression : TypedExpression<T>
		{
		public:
			PropertyExpression(PropertyInfo^ prop, GenericExpression^ instance)
			{
				_inner = gcnew PropertyExpressionUntyped(prop, instance);
			}
		};


		/// <summary>
		/// Represents call to a method or constructor with unknown return type
		/// </summary>
		public ref class MethodBaseInvokeExpressionUntyped : GenericExpression
		{
		protected:
			MethodBase^ _method;
			GenericExpression^ _instance;
			array<GenericExpression^>^ _parameters;

		public:
			MethodBaseInvokeExpressionUntyped(MethodBase^ method, GenericExpression^ instance, ... array<GenericExpression^>^ parameters)
			{
				_method = method;
				_instance = instance;
				_parameters = parameters;
			}

			/// <summary> 
			/// Returns LINQ representation of the expression. 
			/// </summary>
			virtual System::Expressions::Expression^ ToLinq() override 
			{
				List<System::Expressions::Expression^>^ args = gcnew List<System::Expressions::Expression^>();
				if (_parameters != nullptr)
				{
					for each(GenericExpression^ e in _parameters) args->Add(e->ToLinq());
				}
				
				System::Expressions::Expression^ self = nullptr;
				if (_instance) self = _instance->ToLinq();

				if (_method->IsConstructor)
				{
					// Handle bindings (PropMap attribute)
					ConstructorInfo^ ctor = dynamic_cast<ConstructorInfo^>(_method);
					cli::array<ParameterInfo^>^ params = ctor->GetParameters();
					List<System::Expressions::Binding^>^ bindings = gcnew List<System::Expressions::Binding^>(params->Length);
					bool hasBindings = true;

					for(int i=0; i<params->Length; i++)
					{
						cli::array<Object^>^ attrs = params[i]->GetCustomAttributes(PropMapAttribute::typeid, false);
						if (attrs->Length == 0) { hasBindings = false; break; }

						PropMapAttribute^ pm = dynamic_cast<PropMapAttribute^>(attrs[0]);
						MemberInfo^ member = ctor->DeclaringType->GetProperty(pm->Property);
						bindings->Add(System::Expressions::Expression::Bind(member, args[i]));
					}

					if (hasBindings)
					{
						// TODO: May CTP bug workaround (can't use MemberInit and call Ctor in one expression)
						ConstructorInfo^ emptyCtor = ctor->DeclaringType->GetConstructor(Type::EmptyTypes);

						return System::Expressions::Expression::MemberInit
							(System::Expressions::Expression::New(emptyCtor), bindings);
					}
					else
						return System::Expressions::Expression::New(ctor, args);
				}
				else if (_method->IsVirtual)
					return System::Expressions::Expression::CallVirtual(dynamic_cast<MethodInfo^>(_method), self, args);
				else
					return System::Expressions::Expression::Call(dynamic_cast<MethodInfo^>(_method), self, args);
			}
		};

		/// <summary>
		/// Represents call to a method or constructor with known return type
		/// </summary>
		generic<typename T> public ref class MethodBaseInvokeExpression : TypedExpression<T>
		{
		public:
			MethodBaseInvokeExpression(MethodBase^ method, GenericExpression^ instance, ... array<GenericExpression^>^ parameters)
			{
				_inner = gcnew MethodBaseInvokeExpressionUntyped(method, instance, parameters);
			}
		};


		/// <summary>
		/// Represents call to a method with unknown return type
		/// </summary>
		public ref class MethodInvokeExpressionUntyped : MethodBaseInvokeExpressionUntyped
		{
		public:
			MethodInvokeExpressionUntyped(MethodInfo^ method, GenericExpression^ instance, array<GenericExpression^>^ parameters) 
				: MethodBaseInvokeExpressionUntyped(method, instance, parameters) { }
			MethodInvokeExpressionUntyped(MethodInfo^ method, GenericExpression^ instance, GenericExpression^ param) 
				: MethodBaseInvokeExpressionUntyped(method, instance, nullptr ) 
				{ _parameters = gcnew cli::array<GenericExpression^>{ param }; }
		};


		/// <summary>
		/// Represents call to a method with known return type
		/// </summary>
		generic<typename T> public ref class MethodInvokeExpression : MethodBaseInvokeExpression<T>
		{
		public:
			MethodInvokeExpression(MethodInfo^ method, GenericExpression^ instance, ... array<GenericExpression^>^ parameters) 
				: MethodBaseInvokeExpression(method, instance, parameters) { }
			MethodInvokeExpression(MethodInfo^ method, GenericExpression^ instance, GenericExpression^ param) 
				: MethodBaseInvokeExpression(method, instance, param ) 
				{ }
		};

		/// <summary>
		/// Represents call to a constructor with unknown return type
		/// </summary>
		public ref class ConstructorInvokeExpressionUntyped : MethodBaseInvokeExpressionUntyped
		{
		public:
			ConstructorInvokeExpressionUntyped(ConstructorInfo^ ctor, ... array<GenericExpression^>^ parameters) 
				: MethodBaseInvokeExpressionUntyped(ctor, nullptr, parameters) { }
		};

		/// <summary>
		/// Represents call to a constructor with known return type
		/// </summary>
		generic<typename T> public ref class ConstructorInvokeExpression : MethodBaseInvokeExpression<T>
		{
		public:
			ConstructorInvokeExpression(ConstructorInfo^ ctor, ... array<GenericExpression^>^ parameters) 
				: MethodBaseInvokeExpression(ctor, nullptr, parameters) { }
		};

		/// <summary>
		/// Represents binary operation (for example +, -, * and many other)
		/// </summary>
		generic<typename T, typename TL, typename TR> public ref class BinaryExpression : Expression<T>
		{
		private:
			Expression<TL>^ _left;
			Expression<TR>^ _right;
			ExpressionType _type;

		public:
			BinaryExpression(Expression<TL>^ left, Expression<TR>^ right, ExpressionType type)
			{
				_left = left;
				_right = right;
				_type = type;
			}

			/// <summary> 
			/// Returns LINQ representation of the expression. 
			/// </summary>
			virtual System::Expressions::Expression^ ToLinq() override 
			{
				if (TR::typeid == TL::typeid && TL::typeid == String::typeid && _type == ExpressionType::Add) 
				{
					// String concatenation
					MethodInfo^ meth = (String::typeid)->GetMethod("Concat", gcnew array<Type^>{ cli::array<String^>::typeid });
					return (MethodInvokeExpression<T>(meth, nullptr, gcnew ParamsExpression<String^>
						(dynamic_cast<Expression<String^>^>(_left), dynamic_cast<Expression<String^>^>(_right)))).ToLinq();
				}
				else
				{
					System::Expressions::Expression^ leftVal = _left?(_left->ToLinq()):(System::Expressions::Expression::Constant(nullptr));
					System::Expressions::Expression^ rightVal = _right?(_right->ToLinq()):(System::Expressions::Expression::Constant(nullptr));

					if (_type == ExpressionType::Add) return System::Expressions::Expression::Add(leftVal, rightVal);
					if (_type == ExpressionType::Subtract) return System::Expressions::Expression::Subtract(leftVal, rightVal);
					if (_type == ExpressionType::Multiply) return System::Expressions::Expression::Multiply(leftVal, rightVal);
					if (_type == ExpressionType::Divide) return System::Expressions::Expression::Divide(leftVal, rightVal);
					if (_type == ExpressionType::RShift) return System::Expressions::Expression::RShift(leftVal, rightVal);
					if (_type == ExpressionType::LShift) return System::Expressions::Expression::LShift(leftVal, rightVal);
					if (_type == ExpressionType::LT) return System::Expressions::Expression::LT(leftVal, rightVal);
					if (_type == ExpressionType::GT) return System::Expressions::Expression::GT(leftVal, rightVal);
					if (_type == ExpressionType::GE) return System::Expressions::Expression::GE(leftVal, rightVal);
					if (_type == ExpressionType::LE) return System::Expressions::Expression::LE(leftVal, rightVal);
					if (_type == ExpressionType::EQ) return System::Expressions::Expression::EQ(leftVal, rightVal);
					if (_type == ExpressionType::NE) return System::Expressions::Expression::NE(leftVal, rightVal);
					if (_type == ExpressionType::Or) return System::Expressions::Expression::Or(leftVal, rightVal);
					if (_type == ExpressionType::And) return System::Expressions::Expression::And(leftVal, rightVal);
				}
				throw gcnew NotImplementedException("This binary operation is not supported!");
			}
		};


		/// <summary>
		/// Represents unary operations (like boolean negation)
		/// </summary>
		generic<typename T, typename PT> public ref class UnaryExpression : Expression<T>
		{
		private:
			Expression<PT>^ _value;
			ExpressionType _type;

		public:
			UnaryExpression(Expression<PT>^ value, ExpressionType type)
			{
				_value = value;
				_type = type;
			}

			/// <summary> 
			/// Returns LINQ representation of the expression. 
			/// </summary>
			virtual System::Expressions::Expression^ ToLinq() override 
			{
				if (_type == ExpressionType::Not) return System::Expressions::Expression::Not(_value->ToLinq());
				throw gcnew NotImplementedException("This unary operation is not supported!");
			}
		};


		/// <summary>
		/// Base class for the Expr template. Wraps another type of expression.
		/// </summary>
		generic<typename T> public ref class ExpressionWrapper abstract : Expression<T>
		{
		protected:
			Expression<T>^ _value;

		public:
			/// <summary> 
			/// Returns LINQ representation of the expression. 
			/// </summary>
			virtual System::Expressions::Expression^ ToLinq() override 
			{
				return _value->ToLinq();
			}
		};


		/// <summary>
		/// Represents variable of type T
		/// </summary>
		generic<typename T> public ref class Variable : Expression<T>
		{
		private:
			String^ _name;
			System::Expressions::Expression^ _var;

		public:
			Variable(String^ name)
			{
				_name = name;
				_var = System::Expressions::Expression::Parameter(T::typeid, _name);
			}

			/// <summary> 
			/// Returns LINQ representation of the expression. 
			/// </summary>
			virtual System::Expressions::Expression^ ToLinq() override 
			{
				return _var;
			}
		};


		/// <summary>
		/// Represents literal of type T
		/// </summary>
		generic<typename T> public ref class Literal : Expression<T>
		{
		private:
			T _value;

		public:
			Literal(T value)
			{
				_value = value;
			}

			/// <summary> 
			/// Returns LINQ representation of the expression. 
			/// </summary>
			virtual System::Expressions::Expression^ ToLinq() override 
			{
				return System::Expressions::Expression::Constant(_value);
			}
		};


		/// <summary>
		/// Represents lambda expression. F type parameter should be one of the System::Query::Func types.
		/// </summary>
		generic<typename F> public ref class Lambda : Expression<F>
		{
		private:
			GenericExpression^ _body;
			List<System::Expressions::ParameterExpression^>^ _params;

		public:
			Lambda(array<GenericExpression^>^ params, GenericExpression^ body)
			{
				_body = body;
				_params = gcnew List<System::Expressions::ParameterExpression^>();
				for each(GenericExpression^ ge in params) 
					_params->Add((System::Expressions::ParameterExpression^)ge->ToLinq());
			}

			/// <summary> 
			/// Returns LINQ representation of the expression. 
			/// </summary>
			virtual System::Expressions::Expression^ ToLinq() override 
			{
				return System::Expressions::Expression::Lambda(F::typeid, _body->ToLinq(), _params);
			}

			/// <summary> 
			/// Returns typed LINQ representation of the expression. 
			/// </summary>
			System::Expressions::Expression<F>^ ToTypedLinq()
			{
				return System::Expressions::Expression::Lambda<F>(_body->ToLinq(), _params);
			}

			/// <summary> 
			/// Compiles the lambda expression
			/// </summary>
			F Compile()
			{
				System::Expressions::Expression<F>^ lambda = 
					System::Expressions::Expression::Lambda<F>(_body->ToLinq(), _params);
				return lambda->Compile();
			}
		};
	}
}

// Expression::Prop & Expression::Invoke implementations
generic<typename T> generic<typename R> EeekSoft::CLinq::Expression<R>^ EeekSoft::CLinq::Expression<T>::ExpressionInvoke(String^ name, ... cli::array<GenericExpression^>^ args)
{
	Type^ t = T::typeid;
	return gcnew MethodInvokeExpression<R>(t->GetMethod(name), this, args);
}

generic<typename T> generic<typename R> EeekSoft::CLinq::Expression<R>^ EeekSoft::CLinq::Expression<T>::ExpressionProp(String^ name)
{
	Type^ t = T::typeid;
	return gcnew PropertyExpression<R>(t->GetProperty(name), this);
}

generic<typename T> generic<typename R> EeekSoft::CLinq::Expression<R>^ EeekSoft::CLinq::Expression<T>::ExpressionInvokeStatic(String^ name, ... cli::array<GenericExpression^>^ args)
{
	Type^ t = T::typeid;
	return gcnew MethodInvokeExpression<R>(t->GetMethod(name), nullptr, args);
}

generic<typename T> generic<typename R> EeekSoft::CLinq::Expression<R>^ EeekSoft::CLinq::Expression<T>::ExpressionPropStatic(String^ name)
{
	Type^ t = T::typeid;
	return gcnew PropertyExpression<R>(t->GetProperty(name), nullptr);
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)


Written By
Czech Republic Czech Republic
I live in Prague, the capital city of Czech republic (most of the time Smile | :) ). I've been very interested in functional programming recently and I have a passion for the new Microsoft F# language. I'm writing a book about Functional Programming in the Real World that shows the ideas using examples in C# 3.0 and F#.

I've been Microsoft MVP (for C#) since 2004 and I'm one of the most active members of the F# community. I'm a computer science student at Charles University of Prague. My hobbies include photography, fractals and of course many things related to computers (except fixing them). My favorite book writers are Terry Pratchett and Philip K Dick and I like paintings by M. C. Escher.

PS: My favorite codeproject icon is Sheep | [baah] .

Comments and Discussions