Workaround for not having lambdas that can capture managed variables - c++-cli

In C++/CLI, you cannot create managed lambdas (like you can in C#), and thus you can't capture managed variables. You can create regular methods (rather than lambdas), but you are still left without being able to capture managed variables.
Is there a standard workaround to employ in C++/CLI code? In other words I'm looking for a standard pattern I could use in C++/CLI to do the following from C#:
class A { }
class B
{
void Foo()
{
A a = new A();
Func<A> aFunc = () => a; // Captures a
}
}
I could
Create a member variable for each variable I want to capture, and then in the delegate use that member variable. This wouldn't work in the general case as you might have two invocations of the method that want to work on different captured a's, but it would work for the common case.
Create a nested class that does the capturing in its ctor, and then use a method of this nested class as the delegate. This should work in the general case, but it means I need a nested class every time I want to capture different variables.
Question: Is there a better option than the ones above, or which option above would be your go-to approach?
Related Questions:
Lambda expressions as CLR (.NET) delegates / event handlers in Visual C++ 2010
Lambdas in C++/CLI

I wrote Lamda2Delegate struct for this purpose.
Actually it converts c++11 lambda to any .net delegate.
The example of usage:
Thread^ TestLambaWrapper()
{
gcroot<String ^> str = "Testext";
int i = 12345;
Thread^ newThread = gcnew Thread(
Lambda2Delegate<ParameterizedThreadStart>() = [&, str](Object ^ str2)
{
Sleep(2000);
Console::WriteLine("Thread output = {0} {1} {2}", str, i, str2);
}
);
newThread->Start("Nahnah");
return newThread;
}
For your case:
gcroot<A^> a = gcnew A();
Func<A^> ^ aFunc = Lambda2Delegate<>() = [a](){ return (A^)a; };
auto a2 = aFunc();
To capture managed classes you need to wrap them with gcroot, and capture explicitly by value.
And the Lambda2Delegate.h itself
#pragma once
#ifdef _MANAGED
struct AutoDetectDelegateType {};
template<typename TDelegate, typename TLambda, typename TRet, typename ...TParams>
ref class LambdaHolder;
template<typename TDelegate, typename TLambda, typename TRet, typename ...TParams>
ref class LambdaHolder
{
public:
inline LambdaHolder(const TLambda % func) { m_func = new TLambda(func); }
!LambdaHolder() { delete m_func; }
~LambdaHolder() { !LambdaHolder(); }
public:
TRet Callback(TParams... params) { return (*m_func)(params...); }
operator TDelegate ^ () { return gcnew TDelegate(this, &LambdaHolder::Callback); }
private:
TLambda * m_func;
};
template<typename TLambda, typename TRet, typename ...TParams>
ref class LambdaHolder<AutoDetectDelegateType, TLambda, TRet, TParams...>
{
public:
inline LambdaHolder(const TLambda % func) { m_func = new TLambda(func); }
!LambdaHolder() { delete m_func; }
~LambdaHolder() { !LambdaHolder(); }
public:
TRet Callback(TParams... params) { return (*m_func)(params...); }
template<typename TDelegate>
operator TDelegate ^ () { return gcnew TDelegate(this, &LambdaHolder::Callback); }
private:
TLambda * m_func;
};
template <typename TDelegate, typename TLambda>
struct get_labmda_holder : public get_labmda_holder < TDelegate, decltype(&TLambda::operator()) > {};
template <typename TDelegate, typename TLambda, typename TRet, typename... TParams>
struct get_labmda_holder < TDelegate, TRet(__clrcall TLambda::*)(TParams...) const >
{
typedef LambdaHolder<TDelegate, TLambda, TRet, TParams...> TLambdaHolder;
};
template <typename TDelegate, typename TLambda, typename TRet, typename... TParams>
struct get_labmda_holder < TDelegate, TRet(__clrcall TLambda::*)(TParams...) >
{
typedef LambdaHolder<TDelegate, TLambda, TRet, TParams...> TLambdaHolder;
};
template <typename TDelegate, typename TLambda, typename TRet, typename... TParams>
struct get_labmda_holder < TDelegate, TRet(__thiscall TLambda::*)(TParams...) const >
{
typedef LambdaHolder<TDelegate, TLambda, TRet, TParams...> TLambdaHolder;
};
template <typename TDelegate, typename TLambda, typename TRet, typename... TParams>
struct get_labmda_holder < TDelegate, TRet(__thiscall TLambda::*)(TParams...)>
{
typedef LambdaHolder<TDelegate, TLambda, TRet, TParams...> TLambdaHolder;
};
template<typename TDelegate = AutoDetectDelegateType>
struct Lambda2Delegate
{
template<typename TLambda>
typename get_labmda_holder<TDelegate, TLambda>::TLambdaHolder ^ operator = (const TLambda % func)
{
return gcnew get_labmda_holder<TDelegate, TLambda>::TLambdaHolder(func);
}
};
#endif
UPDATE: It is not possible to declare c++ lambda function inside managed member function, but there is workaround - use static member function:
ref class S
{
public:
int F(System::String ^ str)
{
return F(this, str);
}
private:
//static function declaring c++ lambda
static int F(S ^ pThis, System::String ^ str)
{
gcroot<System::String ^> localStr = "local string";
System::Func<System::String ^, int> ^ func = Lambda2Delegate<>() = [=](System::String ^ str)
{
System::Console::WriteLine(str);
System::Console::WriteLine(localStr);
return str->Length;
};
return func(str);
}
};

If you look at a decompilation of a C# lambda, you'll see that the C# compiler does the same thing as your option #2. It's annoying to create a bunch of single-use classes, but that's what I'd recommend.
With a C# lambda, when it creates the nested class instance, it uses that everywhere instead of the local variable. Keep that in mind as you write the method that uses the nested class.

This is my solution for handling lambdas in C++/CLI, with a pretty straightforward syntax. I thought someone else might find it useful:
struct DefaultDelegate;
template<typename... Args>
value struct DelegateType;
template<typename Ret, typename... Args>
value struct DelegateType<DefaultDelegate, Ret, Args...>
{
delegate Ret MyDelegate(Args...);
typedef MyDelegate delegate_type;
};
template<typename Target, typename Ret, typename... Args>
value struct DelegateType<Target, Ret, Args...>
{
typedef Target delegate_type;
};
template<typename Lambda>
ref class LambdaWrapper
{
public:
LambdaWrapper(Lambda &&lambda) : func(new Lambda(std::forward<Lambda>(lambda))) {}
!LambdaWrapper() { delete func; }
~LambdaWrapper() { delete func; }
template<typename Ret, typename... Args>
Ret CallLambda(Args... args) { return (*func)(args...); }
private:
Lambda *func;
};
template<typename Target, typename Lambda, typename Ret, typename... Args>
auto _toDelegate(Lambda &&lambda, Ret(Lambda::*func)(Args...))
{
LambdaWrapper<Lambda> ^lw = gcnew LambdaWrapper<Lambda>(std::forward<Lambda>(lambda));
return gcnew typename DelegateType<Target, Ret, Args...>::delegate_type(lw, &LambdaWrapper<Lambda>::CallLambda<Ret, Args...>);
}
template<typename Target, typename Lambda, typename Ret, typename... Args>
auto _toDelegate(Lambda &&lambda, Ret(Lambda::*func)(Args...) const)
{
LambdaWrapper<Lambda> ^lw = gcnew LambdaWrapper<Lambda>(std::forward<Lambda>(lambda));
return gcnew typename DelegateType<Target, Ret, Args...>::delegate_type(lw, &LambdaWrapper<Lambda>::CallLambda<Ret, Args...>);
}
template<typename Target, typename Lambda>
auto toDelegate(Lambda &&lambda)
{
return _toDelegate<Target>(std::forward<Lambda>(lambda), &Lambda::operator());
}
Usage:
int k = 2;
//If you need a generic delegate
Delegate ^d = toDelegate<DefaultDelegate>([k](int i, int j) ->int {return k * (i + j); });
//If you need a delegate of a specific type
MyDelegate ^d = toDelegate<MyDelegate>([k](int i, int j) ->int {return k * (i + j); });

Related

passing a class variable to API function

I want to track a global variable that I am passing into an API function. I found that one could do it using a class:
template <class T>
class MonitoredVariable
{
public:
MonitoredVariable() {}
MonitoredVariable(const T& value) : m_value(value) {}
//T operator T() const { return m_value; }
const MonitoredVariable& operator = (const T& value)
{
PlugIn::gResultOut << "value changed " << std::endl;
m_value = value;
return *this;
}
private:
T m_value;
};
The API function takes variables as
bool APIFunction(double time, bool *is_done, double *fraction_done);
The following gives me an error:
ImagePtr Im;
bool is_done;
MonitoredVariable<double*> fraction_done;
bool frameready = Im->APIFunction(2.1, is_done, fraction_done);
ERROR:
error C2664: cannot convert argument 3 from 'MonitoredVariable<double *>' to 'double *'
what would I have to change here?
thx!
I'm not really sure if this is what you want:
#include <iostream>
using namespace std;
template <class T>
class MonitoredVariable
{
public:
MonitoredVariable() {}
MonitoredVariable(const T& value) : m_value(value) {}
//T operator T() const { return m_value; }
const MonitoredVariable& operator = (const T& value)
{
//PlugIn::gResultOut << "value changed " << std::endl;
m_value = value.m_value;
return *this;
}
void printValue() {
std::cout << m_value;
}
T& getValue() {
return m_value;
}
private:
T m_value;
};
bool func(double firstDouble, bool *is_done, double* fraction_done) {
// do stuff
*fraction_done = firstDouble + (40.23 * 5);
*is_done = true;
return true;
}
int main()
{
bool is_done = true;
MonitoredVariable<double> fraction_done;
func(2.10, &is_done, &fraction_done.getValue());
fraction_done.printValue();
return 0;
}
So basically we have a Class called MonitoredVariable which has a variable called m_value. I'm not really sure why you wanted it to be a pointer, because we can also take the address of a normal double variable.
In the following it makes perhaps more sense what I want to achieve. I want to input a class variable into an API function and monitor the variable in real time. This value goes from zero to 1 every 3 ms or so. Yet I try to avoid using while loop and track it within the class with overloaded = operator.
#include "stdafx.h"
#include <iostream>
#include <thread>
#include <future>
using namespace std;
template <class T>
class MonitoredVariable
{
public:
MonitoredVariable() {}
MonitoredVariable(const T& value) : m_value(value) {}
void printValue() {
std::cout << m_value;
}
const MonitoredVariable& operator = (const T& value)
{
m_value = value.m_value;
if(m_value> 0.8) std::cout << m_value; // *THIS NEVER GETS PRINTED!!!*
return *this;
}
T& getValue() {
return m_value;
}
private:
T m_value;
};
bool func(bool *is_done, double* fraction_done) {
unsigned long c = 1;
while (*is_done)
{
*fraction_done = (double) 0.01*c;
this_thread::sleep_for(chrono::milliseconds(10));
c++;
if (*fraction_done >= 1) *is_done = false;
}
return true;
}
int main()
{
bool is_done = true;
MonitoredVariable<double> *MonitoredVariablePtr = new MonitoredVariable<double>();
std::future<bool> fu = std::async(std::launch::async,func, &is_done, &MonitoredVariablePtr->getValue());
// IF I UNCOMMENT THIS, IT PRINTS...
/*
while(is_done)
{
if(MonitoredVariablePtr->getValue() > 0.8) MonitoredVariablePtr->printValue();
}
*/
return 0;
}
Why does not the (if(m_value> 0.8) std::cout << m_value) line within the class never gets printed when the value is updated?
THX!

recursive template function does not compile with Clang?

I have a template function which recurses over a parameter pack. In essence it is meant to map something.Get<A,B,C>() into something.Get<A>().Get<B>().Get<C>().
This can be achieved by doing (full stand-alone source below the fold)
template <typename... Pack> class Struct {
std::tuple<Pack...> mTuple;
template <typename Type> auto &GetRef_internal() {
return std::get<first_true<std::is_same<Type, Pack>::value...>::value>(
mTuple);
}
public:
template <typename Current> Current &GetRef() {
return GetRef_internal<Current>();
}
template <typename Current, typename... Next,
typename std::enable_if<sizeof...(Next) != 0>::type * = nullptr>
auto &GetRef() {
auto current = GetRef_internal<Current>();
return current.GetRef<Next...>();
}
};
where first_true returns the index of the first element which is true.
This compiles with g++, and seemingly on MSVC too using an online compiler. When compiling with clang++ I get the following error though:
test.cxx:40:31: error: expected '(' for function-style cast or type construction
return current.GetRef<Next...>();
~~~~^
test.cxx:38:9: error: cannot deduce return type 'auto &' for function with no return statements
auto &GetRef() {
^
test.cxx:48:12: note: in instantiation of function template specialization 'Struct<Struct<int, Struct<float, float> >, Struct<char>, int>::GetRef<Struct<int, Struct<float, float> >, Struct<float, float> , nullptr>' requested here
.GetRef<Struct<int, Struct<float, float>>, Struct<float, float>>();
^
2 errors generated.
What could cause this?
p.s. The actual 'production code' is more useful than the example makes it seem, but that would be too much to post here.
=========================================================================
#include <tuple>
#include <type_traits>
// Template to check if condition holds true for all members of a parameter
// pack.
template <bool... b> struct BoolArray {};
template <bool... b>
using all_true = std::is_same<BoolArray<b...>, BoolArray<(b, true)...>>;
//helper type trait
template <bool... b> struct first_true {
template <
unsigned index = 0,
typename std::enable_if<index<sizeof...(b)-1>::type * =
nullptr> static constexpr unsigned check() {
return std::get<index>(std::make_tuple(b...)) ? index : check<index + 1>();
}
template <unsigned index = 0,
typename std::enable_if<index >= sizeof...(b)-1>::type * = nullptr>
static constexpr unsigned check() {
return std::get<index>(std::make_tuple(b...)) ? index : 0;
}
static constexpr unsigned value = first_true<b...>::check();
};
//The actual problem struct
template <typename... Pack> class Struct {
std::tuple<Pack...> mTuple;
template <typename Type> auto &GetRef_internal() {
return std::get<first_true<std::is_same<Type, Pack>::value...>::value>(
mTuple);
}
public:
template <typename Current> Current &GetRef() {
return GetRef_internal<Current>();
}
template <typename Current, typename... Next,
typename std::enable_if<sizeof...(Next) != 0>::type * = nullptr>
auto &GetRef() {
auto current = GetRef_internal<Current>();
return current.GetRef<Next...>();
}
};
int main() {
// Define a random nested struct
Struct<Struct<int, Struct<float, float>>, Struct<char>, int> myStruct;
// Then retrieve one of the substructures to instantiate the template
auto substruct =
myStruct
.GetRef<Struct<int, Struct<float, float>>, Struct<float, float>>();
return 0;
}
current.GetRef<Next...> is a dependent name, so you need to specify that GetRef names a template using the template keyword:
return current.template GetRef<Next...>();
See Where and why do I have to put the "template" and "typename" keywords? for more information about dependent names.

System::String^ to TCHAR*

I have a class which collects all paths to .txt files of a given folder and stores them into a vector. Most of the functions I use require the usage of TCHAR* to get/set current directory and so on.
The class looks like this:
typedef std::basic_string<TCHAR> tstring;
class folderManager
{
private:
TCHAR searchTemplate[MAX_PATH];
TCHAR directory[MAX_PATH];
WIN32_FIND_DATA ffd;
HANDLE hFind;
vector<tstring> folderCatalog;
vector<tstring> fileNames;
bool succeeded;
public:
// get/set methods and so on...
};
// Changed TCHAR* dir to tstring dir
void folderManager::setDirectory(tstring dir)
{
HANDLE hFind = NULL;
succeeded = false;
folderCatalog.clear();
fileNames.clear();
// Added .c_str()
SetCurrentDirectory(dir.c_str());
GetCurrentDirectoryW(MAX_PATH, directory);
TCHAR fullName[MAX_PATH];
StringCchCat(directory, MAX_PATH, L"\\");
StringCchCopy(searchTemplate, MAX_PATH, directory);
StringCchCat(searchTemplate, MAX_PATH, L"*.txt");
hFind = FindFirstFile(searchTemplate, &ffd);
if (GetLastError() == ERROR_FILE_NOT_FOUND)
{
FindClose(hFind);
return;
}
do
{
StringCchCopy(fullName, MAX_PATH, directory);
StringCchCat(fullName, MAX_PATH, ffd.cFileName);
folderCatalog.push_back(fullName);
fileNames.push_back(ffd.cFileName);
}
while (FindNextFile(hFind, &ffd) != 0);
FindClose(hFind);
succeeded = true;
}
This is where I need to do the conversion of System::String^ to TCHAR*
private: System::Void dienuFolderisToolStripMenuItem_Click(System::Object^
sender, System::EventArgs^ e)
{
FolderBrowserDialog^ dialog;
dialog = gcnew System::Windows::Forms::FolderBrowserDialog;
System::Windows::Forms::DialogResult result = dialog->ShowDialog();
if (result == System::Windows::Forms::DialogResult::OK)
{
// Conversion is now working.
tstring path = marshal_as<tstring>(dialog->SelectedPath);
folder->setDirectory(path);
}
}
marsha_as "Performs the marshaling on a specific data object to convert it between a managed and a native data type".
Here there is the table for possible type conversion.
I use it this way:
marshal_as<std::wstring>(value)
TCHAR can be char or wchar_t, both of them present in marshal_as specialization, I suppose you need to point TCHAR* as template parameter:
TCHAR* result = marshal_as<TCHAR*>(value)
Actually MSDN says that you have to use it this way:
#include <msclr\marshal.h>
using namespace System;
using namespace msclr::interop;
int main(array<System::String ^> ^args)
{
System::String^ managedString = gcnew System::String("Hello World!!!");
marshal_context ^ context = gcnew marshal_context();
const wchar_t* nativeString = context->marshal_as<const wchar_t*>(managedString);
//use nativeString
delete context;
return 0;
}

How do I setup Using Systems::IO::Ports in DLL so that it can be called from a loadlibrary function

Using vc2012 express c++
I am a little confused on how a runtime library works, but I had needed to create one for a driver from some hardware I have so that it can be used in a SDK.
My source code is as follows
#include "PhantomAdapter.h"
#include <stdexcept>
int ready()
{
//return Comms::SerialPort::check();
return 1;
}
int open()
{
int flag=0;
//flag=Comms::SerialPort::openPort();
return flag;
}
int close()
{
Comms::SerialPort::closePort();
return 1;
}
int angle(double& angle)
{
angle = Comms::SerialPort::read();
return 0;
}
int torque(double torque)
{
Comms::SerialPort::send((Byte)torque);
return 1;
}
namespace Comms
{
//static p1 = gcnew System::IO::Ports::SerialPort();
int SerialPort::openPort()
{
bool check=0;
p1 = gcnew System::IO::Ports::SerialPort();
p1->BaudRate = 57600;
p1->PortName = "COM3";
if(p1->IsOpen)
return 0;
else {
p1->Open();
return 1;
}
}
int SerialPort::check()
{
array<String^>^ serialPorts = nullptr;
bool flag = true;
serialPorts = p1->GetPortNames();
for each(String^ port in serialPorts)
{
if(port=="COM3")
flag= true;
}
return flag;
}
void SerialPort::closePort()
{
p1->Close();
}
void SerialPort::send(Byte data)
{
array<unsigned char>^ buffer = gcnew array<Byte>(1);
buffer[0] = (char)data;
p1->Write(buffer,0,1);
}
double SerialPort::read()
{
double data;
data = p1->ReadByte();
return data;
}
}
header
#define PHANTOMADAPTER_API __declspec(dllexport)
#else
#define PHANTOMADAPTER_API __declspec(dllexport)
#endif
#using <mscorlib.dll>
#using <system.dll>
using namespace System;
using namespace System::IO::Ports;
using namespace System::Threading;
extern "C" {
PHANTOMADAPTER_API int ready();
PHANTOMADAPTER_API int open();
PHANTOMADAPTER_API int close();
PHANTOMADAPTER_API int angle(double& angle);
PHANTOMADAPTER_API int torque(double torque);
}
namespace Comms
{
public ref class SerialPort
{
private:
static System::IO::Ports::SerialPort^ p1;
public:
static int openPort();
static void closePort();
static double read();
static void send(Byte data);
static int check();
};
}
I am getting the following error when I call the angle DLL function or any function that requires the Comms namespace.
System.NullReferenceException: Object reference not set to an instance of an object.
at System.IO.Ports.SerialPort.get_IsOpen()
at System.IO.Ports.SerialPort.ReadByte()
at angle(Double* angle)
can someone please point me in the right direction, I feel as if the serialPort class can't be open from runtime library unless I import it somehow

Delaunay triangulation: vertex storing Point_handle instead of Point

I am storing points in a custom container and I would like to build the Delaunay triangulation on a subset of these points.
As the points already exist in the container I don't want the Delaunay triangulation to store copies of these points.
My point class is derived from Point_3 and contains several informations (booleans and ints).
In order to do that, I created a custom triangulation_vertex class :
template < typename GT, typename Pt, typename DSVb = Triangulation_ds_vertex_base_3<> >
class Convection_vertex : public DSVb
{
public:
typedef typename DSVb::Cell_handle Cell_handle;
typedef GT Geom_traits;
typedef typename GT::Point_3 Point;
typedef typename Pt::Point_handle Point_handle;
template < typename TDS2 >
struct Rebind_TDS {
typedef typename DSVb::template Rebind_TDS<TDS2>::Other DSVb2;
typedef Convection_vertex<GT, Pt, DSVb2> Other;
};
private:
static int rank_id;
int number_id;
bool discovered;
Point_handle _ph;
public:
Convection_vertex() : DSVb(), number_id(rank_id++), discovered(false) {}
Convection_vertex(const Point_handle& p) : DSVb(), _ph(p), number_id(rank_id++), discovered(false) {}
Convection_vertex(const Point_handle& p, const Cell_handle& c) : DSVb(c), _ph(p), number_id(rank_id++), discovered(false) {}
Convection_vertex(const Cell_handle& c) : DSVb(c), number_id(rank_id++), discovered(false) {}
const Point& point() const
{ return (*_ph); }
Point& point()
{ return (*_ph); }
void set_point(const Point& p){ }
void set_point(const Point_handle& ph)
{ _ph = ph; }
void set_point_handle(Point_handle ph)
{ _ph = ph; }
const Point_handle& point_handle() const
{ return _ph; }
Point_handle& point_handle()
{ return _ph; }
};
To insert a point in the Delaunay triangulation I do:
DVertex_handle dvh = dt.insert(*p);
dvh->set_point_handle(p);
Where p is a point_handle (ie My_point*).
To delete a point in the Delaunay triangulation I do:
dt.remove(dvh);
where dvh is a vertex_handle.
Inserting points in the triangulation is working fine, but I'm having issues removing points. Is my custom vertex class incorrect ?
Is there a better way to do that ?
--edit-----
dt is the Delaunay triangulation:
typedef CGAL::Convection_vertex<K,Point> Conv_Vb3d;
typedef CGAL::Convection_cell<K> Ce3d;
typedef CGAL::Triangulation_data_structure_3<Conv_Vb3d,Ce3d > Tds3d;
typedef CGAL::Delaunay_triangulation_3<K,Tds3d > Dh;
Dh dt;
--
#sloriot: Is this a good start ?
template < typename CK, bool UseStaticFilters, typename Pt >
struct Convection_traits
: public Filtered_kernel_adaptor<
Type_equality_wrapper<
typename CK:: template Base< Convection_traits<CK, UseStaticFilters,Pt> >::Type,
Convection_traits<CK, UseStaticFilters,Pt> >,
UseStaticFilters >
{
typedef Pt Point_3;
[...] // functors
};
Here is what I have done to use my point_handle as point in the Delaunay_triangulation:
template < typename K_, typename Pt >
class My_traits
{
K_ K;
public:
typedef Pt Point_3;
typedef My_traits<K_, Pt> Self;
//triangulation traits
typedef typename K_::Segment_3 Segment_3;
typedef typename K_::Tetrahedron_3 Tetrahedron_3;
typedef typename K_::Triangle_3 Triangle_3;
typedef typename K_::Construct_segment_3 Construct_segment_3;
typedef typename K_::Construct_triangle_3 Construct_triangle_3;
typedef typename K_::Construct_tetrahedron_3 Construct_tetrahedron_3;
typedef typename K_::Compare_xyz_3 Compare_xyz_3;
typedef typename K_::Coplanar_orientation_3 Coplanar_orientation_3;
typedef typename K_::Orientation_3 Orientation_3;
Construct_tetrahedron_3 construct_tetrahedron_3_object () const{
return K.construct_tetrahedron_3_object ();
}
Construct_triangle_3 construct_triangle_3_object () const{
return K.construct_triangle_3_object ();
}
Construct_segment_3 construct_segment_3_object () const{
return K.construct_segment_3_object ();
}
Compare_xyz_3 compare_xyz_3_object () const{
return K.compare_xyz_3_object ();
}
Coplanar_orientation_3 coplanar_orientation_3_object () const{
return K.coplanar_orientation_3_object ();
}
Orientation_3 orientation_3_object () const{
return K.orientation_3_object ();
}
//delaunay triangulation traits
typedef typename K_::Line_3 Line_3;
typedef typename K_::Object_3 Object_3;
typedef typename K_::Ray_3 Ray_3;
typedef typename K_::Coplanar_side_of_bounded_circle_3 Coplanar_side_of_bounded_circle_3;
typedef typename K_::Side_of_oriented_sphere_3 Side_of_oriented_sphere_3;
typedef typename K_::Compare_distance_3 Compare_distance_3;
Coplanar_side_of_bounded_circle_3 coplanar_side_of_bounded_circle_3_object() const{
return K.coplanar_side_of_bounded_circle_3_object();
}
Side_of_oriented_sphere_3 side_of_oriented_sphere_3_object() const{
return K.side_of_oriented_sphere_3_object();
}
Compare_distance_3 compare_distance_3_object() const{
return K.compare_distance_3_object();
}
};