/* Copyright (c) 1997-2010                   -*- C -*-
   Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Darmstadt, Germany)
   http://www.polymake.de

   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the
   Free Software Foundation; either version 2, or (at your option) any
   later version: http://www.gnu.org/licenses/gpl.txt.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
--------------------------------------------------------------------------------
   $Project: polymake $$Id: CPlusPlus.xs 9432 2010-02-01 23:30:24Z gawrilow $
*/

#include "Ext.h"

HV *pm_perl_FuncDescr_stash=0,
   *pm_perl_TypeDescr_stash=0;
int pm_perl_FuncDescr_fill=0,
    pm_perl_TypeDescr_fill=0;
SV *pm_perl_cur_wrapper_cv=0;	/* FIXME: eventually must be made thread-local */
cpp_basic_vtbl *pm_perl_cur_class_vtbl=0;
GV *pm_perl_cpp_root=0,
   *pm_perl_nesting_level_gv=0;
SV *pm_perl_neg_ind_key=0;

#ifdef PERL_IMPLICIT_CONTEXT
aTHXtype pm_perl_aTHXvar;
#endif

int pm_perl_TypeDescr_pkg_index, pm_perl_TypeDescr_vtbl_index,
    pm_perl_CPPOptions_builtin_index, pm_perl_CPPOptions_descr_index,
    pm_perl_FuncDescr_wrapper_index, pm_perl_FuncDescr_func_ptr_index,
    pm_perl_PropertyType_pkg_index, pm_perl_PropertyType_application_index, pm_perl_PropertyType_cppoptions_index,
    pm_perl_CPP_functions_index, pm_perl_CPP_regular_functions_index,
    pm_perl_CPP_embedded_property_types_index,  pm_perl_CPP_embedded_rules_index,
    pm_perl_CPP_classes_index, pm_perl_CPP_templates_index, pm_perl_CPP_typeids_index,
    pm_perl_CPP_auto_convert_constructor_index, pm_perl_CPP_auto_assignment_index, pm_perl_CPP_auto_conversion_index,
    pm_perl_CPP_auto_assoc_methods_index, pm_perl_CPP_auto_set_methods_index,
    pm_perl_CPP_assoc_helem_index, pm_perl_CPP_assoc_find_index, pm_perl_CPP_assoc_exists_index,
    pm_perl_CPP_assoc_delete_void_index, pm_perl_CPP_assoc_delete_ret_index,
    pm_perl_Application_cpp_index;

static SV *sv_two, *sv_three;

int pm_perl_cpp_free(pTHX_ SV* sv, MAGIC* mg)
{
   if (mg->mg_len) {
      cpp_basic_vtbl *t=(cpp_basic_vtbl*)mg->mg_virtual;
      if (t->destructor) (t->destructor)(mg->mg_ptr);
   }
   return 0;
}

int pm_perl_cpp_dup(pTHX_ MAGIC* mg, CLONE_PARAMS* param)
{
   return 0;
}

static MGVTBL magic_lval_vtbl={ 0, &pm_perl_cpp_assigned, 0, 0, 0 };

/* had to be copied from mg.c */
struct magic_state {
    SV* mgs_sv;
    U32 mgs_flags;
    I32 mgs_ss_ix;
};

static
void defuse_lval_magic(pTHX_ SV* sv)
{
   MGS *mgs;
   I32 mgs_ix;
   assert(PL_savestack[PL_savestack_ix-1].any_i32==SAVEt_DESTRUCTOR_X);
   mgs_ix=PL_savestack[PL_savestack_ix-2].any_i32;
   mgs=SSPTR(mgs_ix, MGS*);
   assert(mgs->mgs_sv==sv);
   mgs->mgs_flags=0;
   SvMAGIC(sv)=0;
}

int pm_perl_cpp_assigned(pTHX_ SV* lval_sv, MAGIC* lval_mg)
{
   SV *sv=lval_mg->mg_obj;
   MAGIC *mg=get_cpp_magic(sv);
   int local=PL_localizing;

   if (local) {
      /* can happen during map or foreach iteration over a container,
	 some nested function tries to localize $_ */
      if (local==1) defuse_lval_magic(aTHX_ lval_sv);
   } else if (mg->mg_flags & value_read_only) {
      croak("Attempt to modify a read-only C++ object");
   } else {
      cpp_basic_vtbl *t=(cpp_basic_vtbl*)mg->mg_virtual;
      if ((t->assignment)(mg->mg_ptr, lval_sv, value_not_trusted))
	 croak(Nullch);
      if (SvROK(lval_sv)) {
	 if (SvRV(lval_sv)==sv) return 0;
	 SvREFCNT_dec(SvRV(lval_sv));
      } else {
	 if (SvPOK(lval_sv) && SvPVX(lval_sv) && SvLEN(lval_sv)) {
	    Safefree(SvPVX(lval_sv));
	    SvPVX(lval_sv)=0;
	    SvLEN(lval_sv)=0;
	 }
	 SvFLAGS(lval_sv) &= ~SVf_OK;
	 SvROK_on(lval_sv);
      }
      SvRV(lval_sv)=SvREFCNT_inc(sv);
   }
   return 0;
}

int pm_perl_primitive_assigned(pTHX_ SV* lval_sv, MAGIC* lval_mg)
{
   int local=PL_localizing;
   if (local) {
      /* can happen during map or foreach iteration over a container,
	 some nested function tries to localize $_ */
      if (local==1) defuse_lval_magic(aTHX_ lval_sv);
   } else if (lval_mg->mg_flags & value_read_only) {
      croak("Attempt to modify an element in a read-only C++ object");
   } else {
      cpp_basic_vtbl *t=(cpp_basic_vtbl*)lval_mg->mg_virtual;
      if ((t->assignment)(lval_mg->mg_ptr, lval_sv, value_not_trusted))
	 croak(Nullch);
   }
   return 0;
}

U32 pm_perl_cpp_container_size(pTHX_ SV* sv, MAGIC* mg)
{
   cpp_container_vtbl *t=(cpp_container_vtbl*)mg->mg_virtual;
   if (mg->mg_flags & value_read_only) {
      if (AvMAX(sv)<0)
	 AvMAX(sv)=(t->size)(mg->mg_ptr);
      return AvMAX(sv)-1;	/* compatible to AvFILL */
   }
   return (t->size)(mg->mg_ptr)-1;
}

static
void destroy_iterators(pTHX_ AV* av, MAGIC* mg)
{
   SV *it_sv=AvARRAY(av)[0];
   cpp_container_access_vtbl *acct=((cpp_container_vtbl*)mg->mg_virtual)->acc + (mg->mg_flags & value_read_only);
   if (SvIOK(it_sv)) {
      if (acct->destructor) (acct->destructor)(SvPVX(it_sv));
      SvIOK_off(it_sv);
   }
   acct+=2;
   if (acct->begin) {
      it_sv=AvARRAY(av)[1];
      if (SvIOK(it_sv)) {
	 if (acct->destructor) (acct->destructor)(SvPVX(it_sv));
	 SvIOK_off(it_sv);
      }
   }
}

static
void destroy_assoc_iterator(pTHX_ HV* hv, MAGIC* mg)
{
   cpp_container_access_vtbl *acct=((cpp_container_vtbl*)mg->mg_virtual)->acc + (mg->mg_flags & value_read_only);
   char *it=(char*)HvARRAY(hv);
#if PerlVersion>=5010
   if (it[acct->obj_size]) {
      if (acct->destructor) (acct->destructor)(it);
      Zero(it,HvMAX(hv)+1,HE*);
   }
#else
   if (it) {
      if (acct->destructor) (acct->destructor)(it);
      Safefree(it);
      HvARRAY(hv)=0;
   }
#endif
}

int pm_perl_cpp_container_clear(pTHX_ SV* sv, MAGIC* mg)
{
   if (mg->mg_flags & value_read_only)
      croak("Attempt to modify a read-only C++ object");
   destroy_iterators(aTHX_ (AV*)sv, mg);
   AvMAX(sv)=-1;
   return 1;
}

int pm_perl_cpp_assoc_clear(pTHX_ SV *sv, MAGIC* mg)
{
   cpp_container_vtbl *t=(cpp_container_vtbl*)mg->mg_virtual;
   if (mg->mg_flags & value_read_only)
      croak("Attempt to modify a read-only C++ object");
   destroy_assoc_iterator(aTHX_ (HV*)sv, mg);
   if ((t->resize)(mg->mg_ptr, 0)) croak(Nullch);
   return 1;
}

int pm_perl_cpp_container_free(pTHX_ SV *sv, MAGIC* mg)
{
   destroy_iterators(aTHX_ (AV*)sv, mg);
   return pm_perl_cpp_free(aTHX_ sv, mg);
}

int pm_perl_cpp_assoc_container_free(pTHX_ SV *sv, MAGIC* mg)
{
   destroy_assoc_iterator(aTHX_ (HV*)sv, mg);
   return pm_perl_cpp_free(aTHX_ sv, mg);
}

U32 pm_perl_cpp_composite_size(pTHX_ SV *sv, MAGIC* mg)
{
   cpp_composite_vtbl *t=(cpp_composite_vtbl*)mg->mg_virtual;
   return t->n_elems-1;		/* compatible to AvFILL */
}

#ifdef SVs_PADBUSY
#  define SaveSVflags (SVs_PADBUSY | SVs_PADTMP | SVs_PADMY | SVs_TEMP)
#else
#  define SaveSVflags (SVs_PADTMP | SVs_PADMY | SVs_TEMP)
#endif

static
SV* new_magic_ref(pTHX_ SV *dst_ref, SV *sv, SV *pkg_ref, int flags)
{
   if (!(SvTYPE(dst_ref)==SVt_PVLV && (LvTYPE(dst_ref)=='t' || LvTYPE(dst_ref)==0))) {
      if (SvTYPE(dst_ref)) {
	 U32 refc=SvREFCNT(dst_ref),
	     save_flags=SvFLAGS(dst_ref) & SaveSVflags;
	 SvREFCNT(dst_ref)=0;
	 sv_clear(dst_ref);
	 SvREFCNT(dst_ref)=refc;
	 SvFLAGS(dst_ref)=save_flags;
      }
      sv_upgrade(dst_ref, flags & value_expect_lval ? SVt_PVLV : SVt_RV);
   }
   SvRV_set(dst_ref,sv);
   SvROK_on(dst_ref);
   if (flags & value_expect_lval)
      sv_magicext(dst_ref, sv, PERL_MAGIC_ext, &magic_lval_vtbl, 0, 0);

   return SvROK(pkg_ref) ? sv_bless(dst_ref, (HV*)SvRV(pkg_ref)) : dst_ref;
}

#undef SaveSVflags

static inline
SV* new_builtin_magic_sv(pTHX_ cpp_basic_vtbl* t, int flags)
{
   SV *sv=newSV(0);
   MAGIC *mg;
   sv_upgrade(sv, SVt_PVMG);
   mg=sv_magicext(sv, 0, PERL_MAGIC_ext, (MGVTBL*)t, 0, flags&value_alloc_magic ? t->obj_size : 0);
   if (flags&value_alloc_magic) New(0, mg->mg_ptr, t->obj_size, char);
   return sv;
}

SV* pm_perl_new_builtin_magic_sv(pTHX_ SV* dst_ref, SV* descr_ref, int flags)
{
   SV **descr=AvARRAY((AV*)SvRV(descr_ref));
   return new_magic_ref(aTHX_ dst_ref,
			new_builtin_magic_sv(aTHX_ (cpp_basic_vtbl*)SvPVX(descr[pm_perl_TypeDescr_vtbl_index]), flags),
			descr[pm_perl_TypeDescr_pkg_index], flags);
}

SV* pm_perl_clone_builtin_magic_sv(pTHX_ SV* src)
{
   MAGIC *mg=SvMAGIC(src);
   return sv_bless(newRV_noinc(new_builtin_magic_sv(aTHX_ (cpp_basic_vtbl*)mg->mg_virtual, value_alloc_magic)), SvSTASH(src));
}

static inline
SV* prepare_scalar_magic_sv(pTHX_ SV *sv, cpp_basic_vtbl* t, int flags)
{
   MAGIC *mg=sv_magicext(sv, 0, PERL_MAGIC_ext, (MGVTBL*)t, 0, flags&value_alloc_magic ? t->obj_size : 0);
   mg->mg_flags |= (flags | SvIVX(t->typeid_name_sv))&value_read_only;
   SvRMAGICAL_on(sv);
   if (flags&value_alloc_magic) New(0, mg->mg_ptr, t->obj_size, char);
   return sv;
}

static inline
SV* new_scalar_magic_sv(pTHX_ cpp_basic_vtbl* t, int flags)
{
   SV *sv=newSV(0);
   sv_upgrade(sv, SVt_PVMG);
   return prepare_scalar_magic_sv(aTHX_ sv,t,flags);
}

SV* pm_perl_new_scalar_magic_sv(pTHX_ SV* dst_ref, SV* descr_ref, int flags)
{
   SV **descr=AvARRAY((AV*)SvRV(descr_ref));
   return new_magic_ref(aTHX_ dst_ref,
			new_scalar_magic_sv(aTHX_ (cpp_basic_vtbl*)SvPVX(descr[pm_perl_TypeDescr_vtbl_index]), flags),
			descr[pm_perl_TypeDescr_pkg_index], flags);
}

SV* pm_perl_clone_scalar_magic_sv(pTHX_ SV* src)
{
   MAGIC *mg=SvMAGIC(src);
   SV *nsv= SvFLAGS(src) & (SVf_ROK|SVf_POK|SVp_POK|SVf_IOK|SVp_IOK|SVf_NOK|SVp_NOK)
          ? prepare_scalar_magic_sv(aTHX_ newSVsv(src), (cpp_basic_vtbl*)mg->mg_virtual, value_alloc_magic)
          : new_scalar_magic_sv(aTHX_ (cpp_basic_vtbl*)mg->mg_virtual, value_alloc_magic);
   return sv_bless(newRV_noinc(nsv), SvSTASH(src));
}

static inline
SV* new_container_magic_sv(pTHX_ cpp_container_vtbl* t, int flags)
{
   MAGIC *mg;
   AV *av=newAV();

   /* has reverse_iterator? */
   int last_it= t->acc[2].begin ? 1 : 0;
   av_extend(av,last_it);
   AvFILLp(av)=last_it;
   AvMAX(av)=-1;

   mg=sv_magicext((SV*)av, 0, PERL_MAGIC_tied, (MGVTBL*)t, 0, flags&value_alloc_magic ? t->obj_size : 0);
   mg->mg_flags |= MGf_COPY | ((flags | SvIVX(t->typeid_name_sv))&value_read_only);
   SvRMAGICAL_on(av);
   if (flags&value_alloc_magic) New(0, mg->mg_ptr, t->obj_size, char);
   return (SV*)av;
}

static inline
SV* new_composite_magic_sv(pTHX_ cpp_composite_vtbl* t, int flags)
{
   MAGIC *mg;
   AV *av=newAV();
   mg=sv_magicext((SV*)av, 0, PERL_MAGIC_tied, (MGVTBL*)t, 0, flags&value_alloc_magic ? t->obj_size : 0);
   mg->mg_flags |= MGf_COPY | ((flags | SvIVX(t->typeid_name_sv))&value_read_only);
   SvRMAGICAL_on(av);
   if (flags&value_alloc_magic) New(0, mg->mg_ptr, t->obj_size, char);
   return (SV*)av;
}

SV* pm_perl_new_container_magic_sv(pTHX_ SV* dst_ref, SV* descr_ref, int flags)
{
   SV **descr=AvARRAY((AV*)SvRV(descr_ref));
   return new_magic_ref(aTHX_ dst_ref,
			new_container_magic_sv(aTHX_ (cpp_container_vtbl*)SvPVX(descr[pm_perl_TypeDescr_vtbl_index]), flags),
			descr[pm_perl_TypeDescr_pkg_index], flags);
}

SV* pm_perl_clone_container_magic_sv(pTHX_ SV* src)
{
   MAGIC *mg=get_cpp_magic(src);
   return sv_bless(newRV_noinc(new_container_magic_sv(aTHX_ (cpp_container_vtbl*)mg->mg_virtual, value_alloc_magic)), SvSTASH(src));
}

SV* pm_perl_new_composite_magic_sv(pTHX_ SV* dst_ref, SV* descr_ref, int flags)
{
   SV **descr=AvARRAY((AV*)SvRV(descr_ref));
   return new_magic_ref(aTHX_ dst_ref,
			new_composite_magic_sv(aTHX_ (cpp_composite_vtbl*)SvPVX(descr[pm_perl_TypeDescr_vtbl_index]), flags),
			descr[pm_perl_TypeDescr_pkg_index], flags);
}

SV* pm_perl_clone_composite_magic_sv(pTHX_ SV* src)
{
   MAGIC *mg=get_cpp_magic(src);
   return sv_bless(newRV_noinc(new_composite_magic_sv(aTHX_ (cpp_composite_vtbl*)mg->mg_virtual, value_alloc_magic)), SvSTASH(src));
}

static inline
SV* new_assoc_container_magic_sv(pTHX_ cpp_container_vtbl* t, int flags)
{
   HV *hv=newHV();
#if PerlVersion>=5010
   cpp_container_access_vtbl *acct=t->acc+(flags & value_read_only);
   /* let it reserve at least one additional byte after the iterator to hold the 'iterator created' flag */
   HvMAX(hv)=acct->obj_size/sizeof(HE*)+1;
   hv_iterinit(hv);
#endif
   MAGIC *mg=sv_magicext((SV*)hv, 0, PERL_MAGIC_tied, (MGVTBL*)t, 0, flags&value_alloc_magic ? t->obj_size : 0);
   mg->mg_flags |= MGf_COPY | ((flags | SvIVX(t->typeid_name_sv))&value_read_only);
   SvRMAGICAL_on(hv);
   if (flags&value_alloc_magic) New(0, mg->mg_ptr, t->obj_size, char);
   return (SV*)hv;
}

SV* pm_perl_new_assoc_container_magic_sv(pTHX_ SV* dst_ref, SV* descr_ref, int flags)
{
   SV **descr=AvARRAY((AV*)SvRV(descr_ref));
   return new_magic_ref(aTHX_ dst_ref,
			new_assoc_container_magic_sv(aTHX_ (cpp_container_vtbl*)SvPVX(descr[pm_perl_TypeDescr_vtbl_index]), flags),
			descr[pm_perl_TypeDescr_pkg_index], flags);
}

SV* pm_perl_clone_assoc_container_magic_sv(pTHX_ SV* src)
{
   MAGIC *mg=get_cpp_magic(src);
   return sv_bless(newRV_noinc(new_assoc_container_magic_sv(aTHX_ (cpp_container_vtbl*)mg->mg_virtual, value_alloc_magic)), SvSTASH(src));
}

int pm_perl_cpp_container_access(pTHX_ SV *sv, MAGIC* mg, SV *nsv, const char *dummy, int index)
{
   OPCODE opc=PL_op->op_type;
   cpp_container_vtbl *t=(cpp_container_vtbl*)mg->mg_virtual;
   cpp_basic_vtbl *saved_vtbl=pm_perl_cur_class_vtbl;
   char *obj=mg->mg_ptr, *it;
   cpp_container_access_vtbl *acct=t->acc+(mg->mg_flags & value_read_only);
   AV *my_av=(AV*)sv;
   SV *it_sv, *ret;
   int it_index, it_incr, cix;
   PERL_CONTEXT *cx;

   if (opc==OP_ITER) {
      cix=cxstack_ix;
      cx=cxstack+cix;
      if (cx->blk_loop.iterary==my_av) {
	 /* direct iterating over a C++ container: each loop requires its own iterator */
	 if (PL_op->op_private & OPpITER_REVERSED) {
	    it_index=1;  it_incr=-1;  acct+=2;
	 } else {
	    it_index=0;  it_incr=1;
	 }
	 if (mg->mg_private != cix) {
	    /* new loop detected: need a new iterator */

	    if (!acct->begin)
	       croak("no access in reverse order");

	    if (SvREFCNT(sv)>1) {
	       /* Create a temporary magical array sharing the C++ object and store it as the loop's array.
		  It will be automatically recycled after the loop completion
	       */
	       SvREFCNT_dec(sv);
	       sv=new_container_magic_sv(aTHX_ t, mg->mg_flags & value_read_only);
	       my_av=(AV*)sv;
	       mg=SvMAGIC(sv);
	       mg->mg_ptr=(char*)obj;
	       cx->blk_loop.iterary=my_av;
	    }
	    mg->mg_private=cix;
	    AvARRAY(my_av)[it_index]=it_sv=newSV(0);
	    sv_upgrade(it_sv, SVt_PVIV);
	    sv_grow(it_sv, acct->obj_size);
	    it=SvPVX(it_sv);
	    goto InitIt;
	 }

	 it_sv=AvARRAY(my_av)[it_index];
	 it=SvPVX(it_sv);
	 if ((SvIVX(it_sv)+=it_incr) == index) goto DerefIt;
	 croak("attempt to access array elements out of natural order");
      }

   } else if ((opc==OP_AELEM || opc==OP_ASLICE) && acct->random) {
      int last_index=pm_perl_cpp_container_size(aTHX_ sv, mg);
      if (index<0) index+=last_index+1;
      if (index>=0 && index<=last_index) {
	 pm_perl_cur_class_vtbl=(cpp_basic_vtbl*)t;
	 ret=(acct->random)(obj, 0, index, nsv, (char*)&obj);
	 pm_perl_cur_class_vtbl=saved_vtbl;
	 if (ret) croak(Nullch);
	 return 1;
      } else {
	 croak("index out of range");
      }
   }

   if (index>=0) {
      it_incr=1;  it_index=0;
   } else {
      it_incr=-1; it_index=1;
      acct+=2;
   }
   it_sv=AvARRAY(my_av)[it_index];
   if (SvIOK(it_sv)) {
      /* iterator already created */
      it=SvPVX(it_sv);
      if ((SvIVX(it_sv)+=it_incr) == index) goto DerefIt;
      if (acct->destructor) (acct->destructor)(it);
      SvIOK_off(it_sv);
   } else {
      AvARRAY(my_av)[it_index]=it_sv=newSV(0);
      sv_upgrade(it_sv, SVt_PVIV);
      sv_grow(it_sv, acct->obj_size);
      it=SvPVX(it_sv);
   }

   if (index) {
      if (index==-1) {
	 if (!acct->begin) croak("no access in reverse order");
      } else {
	 if (opc==OP_AELEM || opc==OP_ASLICE)
	    croak("no random access");
	 else
	    croak("attempt to access array elements out of natural order");
      }
   }

 InitIt:
   if ((acct->begin)(it, obj)) croak(Nullch);
   SvIVX(it_sv)=index;
   SvIOK_on(it_sv);

 DerefIt:
   pm_perl_cur_class_vtbl=(cpp_basic_vtbl*)t;
   if (SvOK(nsv))
      /* we are called from av_store (during aassign): nsv carries the RHS value */
      ret=(t->store_at_ref)(obj, it, index, nsv);
   else
      ret=(acct->deref)(obj, it, index, nsv, (char*)&obj);
   pm_perl_cur_class_vtbl=saved_vtbl;
   if (ret) croak(Nullch);
   return 1;
}

int pm_perl_cpp_assoc_value_access(pTHX_ SV* obj_sv, MAGIC* mg, SV *val_sv, const char *key, int klen)
{
   cpp_container_access_vtbl *acct=((cpp_container_vtbl*)mg->mg_virtual)->acc+(mg->mg_flags & value_read_only);
   char *it=(char*)HvARRAY(obj_sv);
   SV *ret;
   cpp_basic_vtbl *saved_vtbl=pm_perl_cur_class_vtbl;
   pm_perl_cur_class_vtbl=(cpp_basic_vtbl*)mg->mg_virtual;
   ret=(acct->deref)(0, it, 1, val_sv, (char*)&it);
   pm_perl_cur_class_vtbl=saved_vtbl;
   if (ret) croak(Nullch);
   return 1;
}

int pm_perl_cpp_composite_access(pTHX_ SV* sv, MAGIC* mg, SV* nsv, const char *dummy, int index)
{
   cpp_composite_vtbl *t=(cpp_composite_vtbl*)mg->mg_virtual;
   char *obj=mg->mg_ptr;
   cpp_composite_access_vtbl *acct=t->acc+index;
   SV *ret;
   cpp_basic_vtbl *saved_vtbl=pm_perl_cur_class_vtbl;

   if (SvOK(nsv)) {
      /* we are called from av_store (during aassign): nsv carries the RHS value */
      if (mg->mg_flags & value_read_only)
	 croak("Attempt to modify a read-only C++ object");
      pm_perl_cur_class_vtbl=(cpp_basic_vtbl*)t;
      ret=(acct->store)(obj, nsv);
   } else {
      pm_perl_cur_class_vtbl=(cpp_basic_vtbl*)t;
      ret=(acct->get[mg->mg_flags & value_read_only])(obj, nsv, (char*)&obj);
   }
   pm_perl_cur_class_vtbl=saved_vtbl;
   if (ret) croak(Nullch);
   return 1;
}

static int get_sizeof(pTHX_ HV *stash)
{
   dSP;
   int s;
   CV *sizeof_cv=GvCV((GV*)*hv_fetch(stash, "sizeof", 6, FALSE));
   PUSHMARK(SP);
   call_sv((SV*)sizeof_cv, G_SCALAR);
   SPAGAIN;
   s=POPi;
   PUTBACK;
   return s;
}

static inline
SV** call_function(pTHX_ SV *cur_wrapper, SV *wrap_sv, SV *func_ptr_sv, SV** SP)
{
   SV *ret;
   SV *saved_cur_wrapper=pm_perl_cur_wrapper_cv;
   pm_perl_cur_wrapper_cv=cur_wrapper;

   PUTBACK;
   if (SvFLAGS(func_ptr_sv) & SVp_POK) {
      indirect_wrapper_type wrapper=(indirect_wrapper_type)SvPVX(wrap_sv);
      ret=wrapper(SvPVX(func_ptr_sv), SP+1, (char*)&ret);
   } else {
      wrapper_type wrapper=(wrapper_type)SvPVX(wrap_sv);
      ret=wrapper(SP+1, (char*)&ret);
   }
   pm_perl_cur_wrapper_cv=saved_cur_wrapper;
   if (ret==&PL_sv_undef) {
      if (SvPVX(ERRSV)[SvCUR(ERRSV)-1] != '\n')
	 sv_catpvn(ERRSV, "\n", 1);
      croak(Nullch);
   }
   SPAGAIN;
   if (ret) PUSHs(ret);
   return SP;
}

MODULE = Polymake::Core::CPlusPlus		PACKAGE = Polymake::Core::CPlusPlus

PROTOTYPES: DISABLE

void
assign_to_cpp_object(obj, value, flags_sv)
   SV *obj;
   SV *value;
   SV *flags_sv;
PPCODE:
{
   SV *ret;
   MAGIC *mg=get_cpp_magic(SvRV(obj));
   cpp_basic_vtbl* t=(cpp_basic_vtbl*)mg->mg_virtual;
   I32 flags= SvTRUE(flags_sv) ? value_trusted : value_not_trusted;
   cpp_basic_vtbl *saved_vtbl=pm_perl_cur_class_vtbl;
   pm_perl_cur_class_vtbl=t;
   ret=(t->assignment)(mg->mg_ptr, value, flags | value_ignore_magic);
   pm_perl_cur_class_vtbl=saved_vtbl;
   if (ret) croak(Nullch);
   PUTBACK;
   PUSHs(obj);
}

void
assign_array_to_cpp_object(obj, ...)
   SV *obj;
PPCODE:
{
   SV *ret;
   MAGIC *mg=get_cpp_magic(SvRV(obj));
   cpp_basic_vtbl* t=(cpp_basic_vtbl*)mg->mg_virtual;
   I32 flags= SvTRUE(ST(items-1)) ? value_trusted : value_not_trusted;
   cpp_basic_vtbl *saved_vtbl=pm_perl_cur_class_vtbl;
   AV *args=Perl_av_fake(aTHX_ items-2, SP+2);
   pm_perl_cur_class_vtbl=t;
   ret=(t->assignment)(mg->mg_ptr, sv_2mortal(newRV_noinc((SV*)args)), flags | value_ignore_magic);
   pm_perl_cur_class_vtbl=saved_vtbl;
   if (ret) croak(Nullch);
   PUTBACK;
   PUSHs(obj);
}

void
convert_to_string(src, ...)
   SV *src;
PPCODE:
{
   SV *result;
   MAGIC *mg=get_cpp_magic(SvRV(src));
   cpp_opaque_vtbl *t=(cpp_opaque_vtbl*)mg->mg_virtual;
   PUTBACK;
   result=(t->to_string)(mg->mg_ptr, 0);
   if (result==&PL_sv_undef) croak(Nullch);
   SPAGAIN;
   PUSHs(result);
}

void
convert_to_serialized(src, ...)
   SV *src;
PPCODE:
{
   SV *result;
   MAGIC *mg=get_cpp_magic(SvRV(src));
   cpp_opaque_vtbl *t=(cpp_opaque_vtbl*)mg->mg_virtual;
   cpp_basic_vtbl *saved_vtbl=pm_perl_cur_class_vtbl;
   pm_perl_cur_class_vtbl=(cpp_basic_vtbl*)t;
   PUTBACK;
   result=(t->to_serialized)(mg->mg_ptr, (char*)&src);
   pm_perl_cur_class_vtbl=saved_vtbl;
   if (result==&PL_sv_undef) croak(Nullch);
   SPAGAIN;
   PUSHs(result);
}

void
get_magic_typeid(x, lval_flags)
   SV *x;
   I32 lval_flags;
PPCODE:
{
   PUSHs(&PL_sv_undef);
   if (SvROK(x) && (x=SvRV(x), SvOBJECT(x))) {
      MAGIC *mg;
      for (mg=SvMAGIC(x); mg; mg=mg->mg_moremagic)
	 if (mg->mg_virtual->svt_dup == &pm_perl_cpp_dup) {
	    cpp_basic_vtbl *t=(cpp_basic_vtbl*)mg->mg_virtual;
	    SETs(!lval_flags || (mg->mg_flags & value_read_only) ? t->const_typeid_name_sv : t->typeid_name_sv);
	    break;
	 }
   }
}

void
must_be_copied(x,is_temp,check_readonly)
   SV *x;
   SV *is_temp;
   SV *check_readonly;
PPCODE:
{
   MAGIC *mg;
   PUSHs(&PL_sv_yes);
   if (SvROK(x) && (x=SvRV(x), SvOBJECT(x) && (mg=get_cpp_magic(x))))
      if (mg->mg_len &&
	  (SvTRUE(is_temp) || ((cpp_basic_vtbl*)mg->mg_virtual)->is_declared) &&
	  (!SvTRUE(check_readonly) || !(mg->mg_flags & value_read_only)))
	 SETs(&PL_sv_no);
}

void
obj_dimension(vtbl_sv)
   SV *vtbl_sv;
PPCODE:
{
   cpp_basic_vtbl *t=(cpp_basic_vtbl*)SvPVX(vtbl_sv);
   PUSHs(sv_2mortal(newSViv(t->obj_dimension)));
}

void
get_type_proto(vtbl_sv, ix)
   SV *vtbl_sv;
   I32 ix;
PPCODE:
{
   SV *result;
   cpp_basic_vtbl *t=(cpp_basic_vtbl*)SvPVX(vtbl_sv);
   cpp_basic_vtbl *saved_vtbl=pm_perl_cur_class_vtbl;
   pm_perl_cur_class_vtbl=t;
   PUTBACK;
   switch (ix) {
   case 0: {
      result=(((cpp_container_vtbl*)t)->provide_key_type)();
      break;
   }
   case 1: {
      result=(((cpp_container_vtbl*)t)->provide_value_type)();
      break;
   }
   case 2: {
      result=(((cpp_composite_vtbl*)t)->provide_elem_types)();
      break;
   }
   case 3: {
      result=sv_2mortal(newSViv(((cpp_composite_vtbl*)t)->n_elems));
      break;
   }
   case 4: {
      result=(((cpp_opaque_vtbl*)t)->provide_serialized_type)();
      break;
   }
   default:
      pm_perl_cur_class_vtbl=saved_vtbl;
      croak("usage: get_type_protos(vtbl, 0..4)");
   }
   pm_perl_cur_class_vtbl=saved_vtbl;
   if (result==&PL_sv_undef) croak(Nullch);
   SPAGAIN;
   if (result)
      PUSHs(result);
   else
      PUSHs(&PL_sv_undef);
}

void
composite_access(src)
   SV *src;
PPCODE:
   src=SvRV(src);
{
   MAGIC *mg=get_cpp_magic(src);
   cpp_composite_vtbl *t=(cpp_composite_vtbl*)mg->mg_virtual;
   SV *result=sv_newmortal();
   SV *ret;
   cpp_basic_vtbl *saved_vtbl=pm_perl_cur_class_vtbl;
   pm_perl_cur_class_vtbl=(cpp_basic_vtbl*)t;
   ret=(t->acc[CvDEPTH(cv)].get[mg->mg_flags & value_read_only])(mg->mg_ptr, result, (char*)&src);
   pm_perl_cur_class_vtbl=saved_vtbl;
   if (ret) croak(Nullch);
   PUSHs(result);
}

void
call_function(...)
PPCODE:
{
   AV *descr=(AV*)CvXSUBANY(cv).any_ptr;
   SV *wrap_sv=AvARRAY(descr)[pm_perl_FuncDescr_wrapper_index];
   SV *func_ptr_sv=AvARRAY(descr)[pm_perl_FuncDescr_func_ptr_index];
   int n_args=SvIVX(func_ptr_sv);
   if (n_args>=0 && items != n_args) {
      PERL_CONTEXT *cx_bottom=cxstack, *cx=cx_bottom+cxstack_ix;
      while (cx >= cx_bottom) {
	 if (CxTYPE(cx)==CXt_SUB) {
	    cv=cx->blk_sub.cv;
	    if (!SkipDebugSub(cv) && !CvANON(cv)) {
	       GV *gv=CvGV(cv);
	       croak("%s::%.*s : got %d argument(s) while %d expected", HvNAME(GvSTASH(gv)),
		     (int)GvNAMELEN(gv), GvNAME(gv), (int)items, n_args);
	    }
	 }
	 --cx;
      }
      croak("ANONYMOUS C++ function : got %d argument(s) while %d expected", (int)items, n_args);
   }
   SP=call_function(aTHX_ (SV*)cv, wrap_sv, func_ptr_sv, SP);
}

void
call_ellipsis_function(...)
PPCODE:
{
   AV *descr=(AV*)CvXSUBANY(cv).any_ptr;
   SV *wrap_sv=AvARRAY(descr)[pm_perl_FuncDescr_wrapper_index];
   SV *func_ptr_sv=AvARRAY(descr)[pm_perl_FuncDescr_func_ptr_index];
   SV **SPbottom=SP;
   int n_args=SvIVX(func_ptr_sv), n_opt_hashes=n_args>>16;
   if (n_args<0) croak("ellipsis function descriptor lacks the number of fixed arguments");
   n_args&=(1<<16)-1;
   SP+=n_args;
   if (items <= n_args) {
      /* items == n_args, otherwise would be caught by Overload */
      /* no trailing arguments - make an empty array */
      XPUSHs(sv_2mortal(newRV_noinc((SV*)newAV())));
   } else {
      int n_trail=items-n_args-n_opt_hashes;
      SV *trailing=sv_2mortal(newRV_noinc((SV*)Perl_av_fake(aTHX_ n_trail, ++SP)));
      if (n_opt_hashes) {
	 if (n_trail>1) {
	    SETs(trailing);
	    while (n_opt_hashes--) {
	       SP[1]=SP[n_trail];
	       ++SP;
	    }
	 } else {
	    if (!n_trail) {
	       int i;
	       for (i=n_opt_hashes; i>0; --i)
		  SP[i]=SP[i-1];
	    }
	    SETs(trailing);
	    SP+=n_opt_hashes;
	 }
      } else {
	 SETs(trailing);
      }
   }
   PUTBACK;
   SP=call_function(aTHX_ (SV*)cv, wrap_sv, func_ptr_sv, SPbottom);
}

void
call_function_once(descr_ref, ...)
   SV *descr_ref;
PPCODE:
{
   AV *descr=(AV*)SvRV(descr_ref);
   SV *wrap_sv=AvARRAY(descr)[pm_perl_FuncDescr_wrapper_index];
   SV *func_ptr_sv=AvARRAY(descr)[pm_perl_FuncDescr_func_ptr_index];
   int n_args=SvIVX(func_ptr_sv);
   if (items-1 != n_args) croak("wrong number of arguments");
   SP=call_function(aTHX_ descr_ref, wrap_sv, func_ptr_sv, SP);
}

void
create_function_wrapper(descr, pkg)
   SV *descr;
   SV *pkg;
PPCODE:
{
   SV *sub=newSV(0);
   HV *stash=0;
   sv_upgrade(sub, SVt_PVCV);
   CvXSUB(sub)=&XS_Polymake__Core__CPlusPlus_call_function;
   CvFLAGS(sub)=CvFLAGS(cv) | CVf_ANON;
   CvXSUBANY(sub).any_ptr=SvRV(descr);
   if (SvPOK(pkg))
      stash=gv_stashpv(SvPVX(pkg), TRUE);
   else if (SvROK(pkg))
      stash=(HV*)SvRV(pkg);
   CvSTASH(sub)=stash;
   PUSHs(sv_2mortal(newRV_noinc(sub)));
}

void
set_number_of_args(cvref, n_args, n_opt_hashes, is_ellipsis, new_wrapper)
   SV *cvref;
   I32 n_args;
   I32 n_opt_hashes;
   SV *is_ellipsis;
   SV *new_wrapper;
PPCODE:
{
   SV *sub=SvRV(cvref);
   AV *descr=(AV*)CvXSUBANY(sub).any_ptr;
   SV *func_ptr_sv=AvARRAY(descr)[pm_perl_FuncDescr_func_ptr_index];
   if (SvIVX(func_ptr_sv)>=0)
      croak("set_number_of_args called repeatedly");
   if (SvTRUE(is_ellipsis)) {
      n_args+=(n_opt_hashes<<16)-(n_opt_hashes+1); /* the last array argument is included in the counting */
      CvXSUB(sub)=&XS_Polymake__Core__CPlusPlus_call_ellipsis_function;
   }
   SvIVX(func_ptr_sv)=n_args;
   if (SvFLAGS(new_wrapper) & SVp_POK) {
      SV *wrapper_sv=AvARRAY(descr)[pm_perl_FuncDescr_wrapper_index];
      SvPVX(wrapper_sv)=SvPVX(new_wrapper);
   }
}

void
overload_clone_op(ref,...)
   SV *ref;
PPCODE:
{
   SV *obj=SvRV(ref);
   if (SvTYPE(ref)==SVt_PVLV) {
      /* It's the result of a lvalue function (like container random access).
	 The second reference to the object is stored in the ref's set-magic.
	 We shall return the same reference, it won't be checked by perl afterwards. */
      ++SP;
   } else {
      MAGIC *mg=get_cpp_magic(obj);
      cpp_basic_vtbl *t=(cpp_basic_vtbl*)mg->mg_virtual;
      if (!(mg->mg_flags & value_read_only) && t->copy_constructor) {
	 /* Should clone only if persistent and really mutable */
	 SV *copy=(t->sv_cloner)(aTHX_ obj);
	 if ((t->copy_constructor)(SvMAGIC(SvRV(copy))->mg_ptr, mg->mg_ptr))
	    croak(Nullch);
	 PUSHs(sv_2mortal(copy));
      } else {
	 ++SP;
      }
   }
}

void
convert_to_int(obj)
   SV *obj;
PPCODE:
{
   PUSHs(sv_2mortal(newSViv(pm_perl_object_int_value(obj))));
}

void
convert_to_float(obj)
   SV *obj;
PPCODE:
{
   PUSHs(sv_2mortal(newSVnv(pm_perl_object_float_value(obj))));
}

void
classify_scalar(x)
   SV *x;
PROTOTYPE: $
PPCODE:
{
   /* @retval: 0 - string, 1 - float, 2 - int, 3 - boolean, undef - the rest */
   if (x==&PL_sv_yes || x==&PL_sv_no) {
      PUSHs(sv_three);
   } else if (SvIOK(x)) {
      PUSHs(sv_two);
   } else if (SvNOK(x)) {
      PUSHs(&PL_sv_yes);
   } else if (SvPOK(x)) {
      int flags;
      if (SvCUR(x)>0 && (flags=looks_like_number(x))) {
	 if ((flags & (IS_NUMBER_NOT_INT|IS_NUMBER_IN_UV))==IS_NUMBER_IN_UV)
	    PUSHs(sv_two);
	 else
	    PUSHs(&PL_sv_yes);
      } else {
	 PUSHs(&PL_sv_no);
      }
   } else {
      PUSHs(&PL_sv_undef);
   }
}

MODULE = Polymake::Core::CPlusPlus		PACKAGE = Polymake::Core::CPlusPlus::Iterator

void
incr(ref, ...)
   SV *ref;
PPCODE:
{
   MAGIC *mg=SvMAGIC(SvRV(ref));
   cpp_iterator_vtbl *t=(cpp_iterator_vtbl*)mg->mg_virtual;
   if ((t->incr)(mg->mg_ptr)) croak(Nullch);
   ++SP;
}

void
not_at_end(ref, ...)
   SV *ref;
PPCODE:
{
   MAGIC *mg=SvMAGIC(SvRV(ref));
   cpp_iterator_vtbl *t=(cpp_iterator_vtbl*)mg->mg_virtual;
   int ret=(t->at_end)(mg->mg_ptr);
   if (ret<0) croak(Nullch);
   if (ret)
      PUSHs(&PL_sv_no);
   else
      PUSHs(&PL_sv_yes);
}

void
deref(ref, ...)
   SV *ref;
PPCODE:
{
   MAGIC *mg=SvMAGIC(SvRV(ref));
   cpp_iterator_vtbl *t=(cpp_iterator_vtbl*)mg->mg_virtual;
   SV *result;
   cpp_basic_vtbl *saved_vtbl=pm_perl_cur_class_vtbl;
   PUTBACK;
   pm_perl_cur_class_vtbl=(cpp_basic_vtbl*)t;
   result=(t->deref)(mg->mg_ptr,(char*)&t);
   pm_perl_cur_class_vtbl=saved_vtbl;
   if (result==&PL_sv_undef) croak(Nullch);
   SPAGAIN;
   PUSHs(result);
}

void
deref_to_scalar(ref, ...)
   SV *ref;
PPCODE:
{
   MAGIC *mg=SvMAGIC(SvRV(ref));
   cpp_iterator_vtbl *t=(cpp_iterator_vtbl*)mg->mg_virtual;
   SV *result;
   cpp_basic_vtbl *saved_vtbl=pm_perl_cur_class_vtbl;
   PUTBACK;
   pm_perl_cur_class_vtbl=(cpp_basic_vtbl*)t;
   result=(t->deref)(mg->mg_ptr,(char*)&t);
   pm_perl_cur_class_vtbl=saved_vtbl;
   if (result==&PL_sv_undef) croak(Nullch);
   SPAGAIN;
   PUSHs(sv_2mortal(newRV(result)));
}

void
hidden(ref, ...)
   SV *ref;
PPCODE:
{
   PUSHs(SvRV(ref));
}

MODULE = Polymake::Core::CPlusPlus		PACKAGE = Polymake::Core::CPlusPlus::TiedArray

void
EXTEND(obj, n)
   SV *obj;
   I32 n;
PPCODE:
{
   MAGIC *mg=get_cpp_magic(SvRV(obj));
   cpp_container_vtbl *t=(cpp_container_vtbl*)mg->mg_virtual;
   if ((mg->mg_flags & value_read_only) || !t->resize) croak("Attempt to overwrite elements in a read_only C++ object");
   if ((t->resize)(mg->mg_ptr, n)) croak(Nullch);
}

MODULE = Polymake::Core::CPlusPlus		PACKAGE = Polymake::Core::CPlusPlus::TiedCompositeArray

void
EXTEND(obj, n)
   SV *obj;
   I32 n;
PPCODE:
{
   MAGIC *mg=get_cpp_magic(SvRV(obj));
   cpp_composite_vtbl *t=(cpp_composite_vtbl*)mg->mg_virtual;
   if (n != t->n_elems) croak("Wrong number of elements in a composite assignment");
}

MODULE = Polymake::Core::CPlusPlus		PACKAGE = Polymake::Core::CPlusPlus::TiedHash

void
FIRSTKEY(obj_ref)
   SV *obj_ref;
PPCODE:
{
   SV *obj_sv=SvRV(obj_ref);
   SV *key_sv=sv_newmortal();
   MAGIC *mg=get_cpp_magic(obj_sv);
   char *obj=mg->mg_ptr, *it=(char*)HvARRAY(obj_sv);
   cpp_container_access_vtbl *acct=((cpp_container_vtbl*)mg->mg_virtual)->acc+(mg->mg_flags & value_read_only);
#if PerlVersion>=5010
   if (it[acct->obj_size]) {
      if (acct->destructor) (acct->destructor)(it);
      it[acct->obj_size]=0;
   }
#else
   if (it) {
      if (acct->destructor) (acct->destructor)(it);
   } else {
      New(0, it, acct->obj_size, char);
      HvARRAY(obj_sv)=(HE**)it;
   }
#endif
   if ((acct->begin)(it, obj)) {
#if PerlVersion<5010
      Safefree(it);
      HvARRAY(obj_sv)=0;
#endif
      croak(Nullch);
   }
#if PerlVersion>=5010
   it[acct->obj_size]=1;
#endif
   if ((acct->deref)(0, it, -1, key_sv, (char*)&it)) croak(Nullch);
   PUSHs(key_sv);
}

void
NEXTKEY(obj_ref,key_sv)
   SV *obj_ref;
   SV *key_sv;
PPCODE:
{
   SV *obj_sv=SvRV(obj_ref);
   MAGIC *mg=get_cpp_magic(obj_sv);
   cpp_container_access_vtbl *acct=((cpp_container_vtbl*)mg->mg_virtual)->acc+(mg->mg_flags & value_read_only);
   char *it=(char*)HvARRAY(obj_sv);
   key_sv=sv_newmortal();
   if ((acct->deref)(0, it, 0, key_sv, (char*)&it)) croak(Nullch);
   PUSHs(key_sv);
}

BOOT:
{
   static const char FuncDescr_name[]="Polymake::Core::CPlusPlus::FuncDescr",
                     TypeDescr_name[]="Polymake::Core::CPlusPlus::TypeDescr";

   pm_perl_cpp_root=gv_fetchpv("Polymake::Core::CPlusPlus::root", FALSE, SVt_PV);
   pm_perl_nesting_level_gv=gv_fetchpv("Polymake::Core::PropertyType::nesting_level", FALSE, SVt_PV);
#ifdef PERL_IMPLICIT_CONTEXT
   pm_perl_aTHXvar=aTHX;
#endif
   pm_perl_FuncDescr_stash=gv_stashpvn(FuncDescr_name, sizeof(FuncDescr_name)-1, FALSE);
   pm_perl_FuncDescr_fill=get_sizeof(aTHX_ pm_perl_FuncDescr_stash)-1;
   pm_perl_FuncDescr_wrapper_index=CvDEPTH(get_cv("Polymake::Core::CPlusPlus::FuncDescr::wrapper", FALSE));
   pm_perl_FuncDescr_func_ptr_index=CvDEPTH(get_cv("Polymake::Core::CPlusPlus::FuncDescr::func_ptr", FALSE));
   pm_perl_TypeDescr_stash=gv_stashpvn(TypeDescr_name, sizeof(TypeDescr_name)-1, FALSE);
   pm_perl_TypeDescr_fill=get_sizeof(aTHX_ pm_perl_TypeDescr_stash)-1;
   pm_perl_TypeDescr_pkg_index=CvDEPTH(get_cv("Polymake::Core::CPlusPlus::TypeDescr::pkg", FALSE));
   pm_perl_TypeDescr_vtbl_index=CvDEPTH(get_cv("Polymake::Core::CPlusPlus::TypeDescr::vtbl", FALSE));
   pm_perl_CPPOptions_builtin_index=CvDEPTH(get_cv("Polymake::Core::CPlusPlus::Options::builtin", FALSE));
   pm_perl_CPPOptions_descr_index=CvDEPTH(get_cv("Polymake::Core::CPlusPlus::Options::descr", FALSE));
   pm_perl_PropertyType_pkg_index=CvDEPTH(get_cv("Polymake::Core::PropertyType::pkg", FALSE));
   pm_perl_PropertyType_application_index=CvDEPTH(get_cv("Polymake::Core::PropertyType::application", FALSE));
   pm_perl_PropertyType_cppoptions_index=CvDEPTH(get_cv("Polymake::Core::PropertyType::cppoptions", FALSE));
   pm_perl_CPP_functions_index=CvDEPTH(get_cv("Polymake::Core::CPlusPlus::functions", FALSE));
   pm_perl_CPP_regular_functions_index=CvDEPTH(get_cv("Polymake::Core::CPlusPlus::regular_functions", FALSE));
   pm_perl_CPP_embedded_rules_index=CvDEPTH(get_cv("Polymake::Core::CPlusPlus::embedded_rules", FALSE));
   pm_perl_CPP_embedded_property_types_index=CvDEPTH(get_cv("Polymake::Core::CPlusPlus::embedded_property_types", FALSE));
   pm_perl_CPP_classes_index=CvDEPTH(get_cv("Polymake::Core::CPlusPlus::classes", FALSE));
   pm_perl_CPP_templates_index=CvDEPTH(get_cv("Polymake::Core::CPlusPlus::templates", FALSE));
   pm_perl_CPP_typeids_index=CvDEPTH(get_cv("Polymake::Core::CPlusPlus::typeids", FALSE));
   pm_perl_CPP_auto_convert_constructor_index=CvDEPTH(get_cv("Polymake::Core::CPlusPlus::auto_convert_constructor", FALSE));
   pm_perl_CPP_auto_assignment_index=CvDEPTH(get_cv("Polymake::Core::CPlusPlus::auto_assignment", FALSE));
   pm_perl_CPP_auto_conversion_index=CvDEPTH(get_cv("Polymake::Core::CPlusPlus::auto_conversion", FALSE));
   pm_perl_CPP_auto_assoc_methods_index=CvDEPTH(get_cv("Polymake::Core::CPlusPlus::auto_assoc_methods", FALSE));
   pm_perl_CPP_auto_set_methods_index=CvDEPTH(get_cv("Polymake::Core::CPlusPlus::auto_set_methods", FALSE));
   pm_perl_CPP_assoc_helem_index=SvIVX(get_sv("Polymake::Core::CPlusPlus::assoc_helem", FALSE));
   pm_perl_CPP_assoc_find_index=SvIVX(get_sv("Polymake::Core::CPlusPlus::assoc_find", FALSE));
   pm_perl_CPP_assoc_exists_index=SvIVX(get_sv("Polymake::Core::CPlusPlus::assoc_exists", FALSE));
   pm_perl_CPP_assoc_delete_void_index=SvIVX(get_sv("Polymake::Core::CPlusPlus::assoc_delete_void", FALSE));
   pm_perl_CPP_assoc_delete_ret_index=SvIVX(get_sv("Polymake::Core::CPlusPlus::assoc_delete_ret", FALSE));
   pm_perl_Application_cpp_index=CvDEPTH(get_cv("Polymake::Core::Application::cpp", FALSE));

   CvLVALUE_on(get_cv("Polymake::Core::CPlusPlus::Iterator::hidden", FALSE));
   if (PL_DBgv) {
      CvNODEBUG_on(get_cv("Polymake::Core::CPlusPlus::Iterator::hidden", FALSE));
   }

   pm_perl_neg_ind_key=newSVpvn_share(NEGATIVE_INDICES_VAR,16,0);

   sv_two=newSViv(2);    SvREADONLY_on(sv_two);
   sv_three=newSViv(3);  SvREADONLY_on(sv_three);
}
