/* Copyright (c) 1997-2010
   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: Value.h 9718 2010-07-08 13:32:42Z gawrilow $
*/

#ifndef _POLYMAKE_PERL_VALUE_H
#define _POLYMAKE_PERL_VALUE_H

#include <limits>
#include <cmath>
#include <string>
#include <assoc.h>
#include <typeinfo>

#include <perl/glue.h>
#include <CharBuffer.h>
#include <GenericIO.h>
#include <Wary.h>

namespace pm { namespace perl {

   class ScalarBase {
   protected:
      SV *sv;

      ScalarBase() : sv(pm_perl_newSV()) {}

      explicit ScalarBase(SV *sv_arg)
	 : sv(sv_arg) {}

      void forget() { pm_perl_decr_SV(sv); }
   public:
      SV* get() const { return sv; }
      SV* get_temp() { return pm_perl_2mortal(sv); }

      void finish() const {}
   };

   class Stack {
   protected:
      SV **stack;

      Stack() : stack(pm_perl_get_stack()) {}
      Stack(SV **stack_arg) : stack(stack_arg) {}

   public:
      void push(ScalarBase& x) { *++stack = x.get_temp(); }

      void finish() const { pm_perl_sync_stack(stack); }
   };

   class Scalar : public ScalarBase {
   public:
      Scalar() {}
      Scalar(const Scalar& x) { pm_perl_set_copy(sv,x.sv); }
      Scalar& operator= (const Scalar& x) { pm_perl_set_copy(sv,x.sv); return *this; }
      ~Scalar() { forget(); }
   };

   class Value;

   class ArrayBase : public ScalarBase {
   public:
      explicit ArrayBase(int prealloc=0)
	 : ScalarBase(pm_perl_newAV(prealloc)) {}

      explicit ArrayBase(SV *sv_arg)
	 : ScalarBase(sv_arg) {}

      explicit ArrayBase(const Value&);

      ArrayBase(const ScalarBase& x, int prealloc)
	 : ScalarBase(x)
      {
	 pm_perl_makeAV(sv,prealloc);
      }

      ArrayBase(SV *sv_arg, value_flags flags)
	 : ScalarBase(sv_arg)
      {
	 if (flags & value_not_trusted) verify();
      }

      void upgrade(int prealloc)
      {
	 pm_perl_makeAV(sv,prealloc);
      }

      void verify() const
      {
	 if (!pm_perl_is_AV_reference(sv))
	    throw std::runtime_error("input argument is not an array");
      }

      int size() const { return pm_perl_AV_size(sv); }

      SV* operator[] (int i) const
      {
	 return *pm_perl_AV_fetch(sv,i);
      }

      SV* shift()
      {
	 return pm_perl_AV_shift(sv);
      }

      SV* pop()
      {
	 return pm_perl_AV_pop(sv);
      }

      void unshift(const ScalarBase& x)
      {
	 pm_perl_AV_unshift(sv,x.get());
      }

      void push(const ScalarBase& x)
      {
	 pm_perl_AV_push(sv,x.get());
      }

      void unshift(SV *x)
      {
	 pm_perl_AV_unshift(sv,x);
      }

      void push(SV *x)
      {
	 pm_perl_AV_push(sv,x);
      }
   };

   class HashBase : public ScalarBase {
   protected:
      HashBase() {}
   public:
      explicit HashBase(SV *sv_arg)
	 : ScalarBase(sv_arg) {}

      void verify()
      {
	 if (!pm_perl_is_HV_reference(sv))
	    throw std::runtime_error("input argument is not a hash");
      }

      bool exists(const std::string& key) const
      {
	 return pm_perl_HV_exists(sv, key.c_str(), key.size());
      }

      template <size_t klen>
      bool exists (const char (&key)[klen]) const
      {
	 return pm_perl_HV_exists(sv, key, klen-1);
      }
   };

   class istreambuf : public streambuf_with_input_width {
   public:
      istreambuf(SV *sv);

      int lines()
      {
	 return CharBuffer::count_lines(this);
      }
   };

   class ostreambuf : public std::streambuf {
   public:
      ostreambuf(SV *sv);
      ~ostreambuf();
   protected:
      SV *val;
      int_type overflow(int_type c=traits_type::eof());
   };

   template <typename StreamBuffer>
   class BufferHolder {
   protected:
      StreamBuffer my_buf;

      explicit BufferHolder(SV *sv) : my_buf(sv) {}
   };

   class istream : public BufferHolder<istreambuf>, public std::istream {
   public:
      explicit istream(SV *sv)
	 : BufferHolder<istreambuf>(sv), std::istream(&my_buf)
      {
	 exceptions(failbit | badbit);
	 if (!pm_perl_get_cur_length(sv))
	    setstate(eofbit);
      }

      void finish()
      {
	 if (good() && CharBuffer::next_non_ws(&my_buf)>=0)
	    setstate(failbit);
      }
   };

   class ostream : public BufferHolder<ostreambuf>, public std::ostream {
   public:
      explicit ostream(SV *sv)
	 : BufferHolder<ostreambuf>(sv), std::ostream(&my_buf)
      {
	 precision(10);
	 exceptions(failbit | badbit);
      }
   };

   class Object; class ObjectType;
   class Array; class Hash;
   class OptionSet;

   struct unknown {};
   template <bool exact_match>
   struct SV4recognize : public SV { protected: SV4recognize(); ~SV4recognize(); };
   template <>
   struct SV4recognize<true> : public SV4recognize<false> { protected: void *dummy; SV4recognize(); ~SV4recognize(); };

   SV* get_type(const char *type_name, size_t tl);
   SV* get_type(SV *prescribed_pkg, const typeinfo& ti, SV *super_proto=0);
   template <typename TypeList, size_t pkgl, bool exact_match>
   SV4recognize<exact_match>* get_type(const char (&pkg)[pkgl], bool2type<exact_match>);
   SV* get_type(const char *pkg, size_t pkgl, SV** (*fill)(SV**), bool exact_match);

   struct undefined : public std::runtime_error {
      undefined();
   };

   class exception : public std::exception {
      const char* what() const throw();
   };
   SV* propagate(const std::exception& ex);
   SV* propagate();

   template <typename X> struct TryCanned;
   template <typename X> struct Canned;
   template <typename X> struct Enum;
   template <typename X> struct ReturningList;

   struct Anything {
      template <typename X> Anything(const X&) {}
   };

   template <typename Options=void> class ValueOutput;
   template <typename Options=void> class ValueInput;

   template <typename ElementType, typename Options>
   class ListValueInput
      : public ArrayBase,
	public GenericInputImpl< ListValueInput<ElementType,Options> >,
	public GenericIOoptions< ListValueInput<ElementType,Options>, Options, 1 > {
      int i, _size, _dim;

      typedef GenericIOoptions< ListValueInput<ElementType,Options>, Options, 1 > _opts;
   public:
      typedef ElementType value_type;

      ListValueInput(SV *sv_arg)
	 : ArrayBase(sv_arg, _opts::template get_option< TrustedValue<True> >::value ? value_trusted : value_not_trusted),
	   i(0), _size(ArrayBase::size()), _dim(-1) {}

      template <typename T> inline
      ListValueInput& operator>> (T& x);

      int size() const { return _size; }
      bool at_end() const { return i>=_size; }

      void finish()
      {
	 if (_opts::template get_option< CheckEOF<False> >::value && !at_end())
	    throw std::runtime_error("list input - size mismatch");
      }

      template <typename T>
      int lookup_lower_dim(bool tell_size_if_dense);

      void skip_item() { ++i; }
      void skip_rest() { i=_size; }

      bool sparse_representation()
      {
	 if (extract_bool_param<Options, SparseRepresentation>::specified)
	    return extract_bool_param<Options, SparseRepresentation>::value;
	 int has_sparse_representation;
	 _dim=pm_perl_get_sparse_dim(sv, &has_sparse_representation);
	 return has_sparse_representation;
      }

      int lookup_dim(bool tell_size_if_dense)
      {
	 return sparse_representation() ? _dim :
	        tell_size_if_dense ? _size : -1;
      }

      int index()
      {
	 if (!sparse_representation())
	    throw std::runtime_error("dense/sparse input mismatch");
	 int ix;
	 *this >> ix;
	 if (!_opts::template get_option< TrustedValue<True> >::value && (ix<0 || ix>=_dim))
	    throw std::runtime_error("sparse index out of range");
	 return ix;
      }
   };

   template <typename Options>
   class ValueInput
      : public ScalarBase,
	public GenericInputImpl< ValueInput<Options> >,
	public GenericIOoptions< ValueInput<Options>, Options > {
      template <typename, typename> friend class ListValueInput;
   public:
      ValueInput(SV *sv_arg)
	 : ScalarBase(sv_arg) {}

      template <typename Data>
      void fallback(Data& x, typename enable_if<int, io_test::has_std_input_operator<Data>::value>::type=0)
      {
	 istream is(sv);
	 is >> x;  is.finish();
      }

      template <typename Data>
      void fallback(Data& x, typename disable_if<int, io_test::has_std_input_operator<Data>::value>::type=0)
      {
	 throw std::runtime_error(std::string("no serialization defined for type ") + typeid(Data).name());
      }

      template <typename ObjectRef>
      struct list_cursor {
	 typedef typename remove_bool_param<Options,SparseRepresentation>::type
	    cursor_options;
	 typedef ListValueInput<typename deref<ObjectRef>::type::value_type, cursor_options>
	    type;
      };

      template <typename ObjectRef>
      struct composite_cursor {
	 typedef typename replace_params<
	         typename remove_bool_param<Options,
					    SparseRepresentation>::type,
	                                    CheckEOF<True> >::type
	    cursor_options;
	 typedef ListValueInput<void, cursor_options> type;
      };

      template <typename T>
      typename list_cursor<T>::type begin_list(T*)
      {
	 return sv;
      }

      template <typename T>
      typename composite_cursor<T>::type begin_composite(T*)
      {
	 return sv;
      }
   };

   template <typename Options, bool _list_return=extract_bool_param<Options, ReturningList>::value>
   class ListValueOutput
      : public ArrayBase,
	public GenericIOoptions< ListValueOutput<Options>, Options > {
      ListValueOutput();
   public:
      typedef ScalarBase super;
      typedef SV* super_arg;
      static const bool stack_based=false;

      template <typename X>
      ListValueOutput& operator<< (const X& x)
      {
	 Value elem;
	 elem << x;
	 push(elem);
	 return *this;
      }
   };

   template <typename Options>
   class ValueOutput
      : public ListValueOutput<Options>::super,
	public GenericOutputImpl< ValueOutput<Options> >,
	public GenericIOoptions< ValueOutput<Options>, Options > {

      typedef typename ListValueOutput<Options>::super super;
      typedef typename ListValueOutput<Options>::super_arg super_arg;
   public:
      template <typename Data>
      void fallback(const Data& x,
		    typename enable_if<int, io_test::has_std_output_operator<Data>::value && !ListValueOutput<Options>::stack_based>::type=0)
      {
	 ostream os(this->sv);
	 os << x;
      }

      template <typename Data>
      void fallback(const Data& x,
		    typename enable_if<int, io_test::has_std_output_operator<Data>::value && ListValueOutput<Options>::stack_based>::type=0)
      {
	 ScalarBase s;
	 ostream os(x.get());
	 os << x;
	 this->push(s);
	 this->finish();
      }

      template <typename Data>
      void fallback(const Data& x, typename disable_if<int, io_test::has_std_output_operator<Data>::value>::type=0)
      {
	 throw std::runtime_error(std::string("no serialization defined for type ") + typeid(Data).name());
      }

      ValueOutput() {}

      explicit ValueOutput(super_arg sv_arg)
	 : super(sv_arg) {}

      template <typename ObjectRef>
      struct list_cursor {
	 typedef ListValueOutput<Options>& type;
      };

      template <typename ObjectRef>
      struct composite_cursor {
	 typedef typename deref<ObjectRef>::type Object;
	 static const int
            total= list_length<typename object_traits<Object>::elements>::value,
	    ignored= list_accumulate_unary<list_count, ignore_in_composite, typename object_traits<Object>::elements>::value;
	 static const bool
            compress= ignored>0 && total-ignored<=1;
	 typedef typename if_else<compress, ValueOutput, ListValueOutput<Options> >::type& type;
      };

      template <typename ObjectRef>
      struct sparse_cursor : list_cursor<ObjectRef> {};

      template <typename T>
      ListValueOutput<Options>& begin_list(const T* x)
      {
	 ListValueOutput<Options>& pvl=static_cast<ListValueOutput<Options>&>(static_cast<super&>(*this));
	 pvl.upgrade(x && !object_traits<T>::is_lazy && container_traits<T>::is_forward ? x->size() : 0);
	 return pvl;
      }

   private:
      template <typename T>
      ListValueOutput<Options>& _begin_composite(const T*, False)
      {
	 ListValueOutput<Options>& pvl=static_cast<ListValueOutput<Options>&>(static_cast<super&>(*this));
	 pvl.upgrade(list_length<typename object_traits<T>::elements>::value);
	 return pvl;
      }

      template <typename T>
      ValueOutput& _begin_composite(const T*, True)
      {
	 return *this;
      }

   public:
      template <typename T>
      typename composite_cursor<T>::type
      begin_composite(const T*)
      {
	 return _begin_composite((const T*)0, bool2type<composite_cursor<T>::compress>());
      }

      template <typename T>
      ListValueOutput<Options>& begin_sparse(const T* x)
      {
	 return begin_list(x);
      }

      template <typename T>
      bool prefer_sparse_representation(const T&) const { return false; }
   };

   class ListReturn :
      public Stack {
   public:
      ListReturn() {}
      ListReturn(SV **stack_arg) : Stack(stack_arg) {}

      template <typename T>
      ListReturn& operator<< (const T& x);
   };

   template <typename Options>
   class ListValueOutput<Options, true>
      : public ListReturn,
	public GenericIOoptions< ListValueOutput<Options, true>, Options > {
      ListValueOutput();
   public:
      typedef Stack super;
      typedef SV** super_arg;
      static const bool stack_based=true;

      void upgrade(int prealloc)
      {
	 stack=pm_perl_extend_stack(stack, prealloc);
      }
   };

   typedef ValueOutput< ReturningList<True> > ListSlurp;
} }
namespace polymake { namespace perl_bindings {
   struct bait {};
   inline pm::perl::unknown* recognize(bait*, ...) { return 0; }
   inline SV* field_names(bait*, ...) { return pm::perl::pm_perl_undef(); }
   template <typename T> class Class;
} }
namespace pm {

   template <>
   struct is_printable<perl::Value> : False {};
   template <>
   struct is_parseable<perl::Value> : False {};

namespace perl {
   typedef polymake::perl_bindings::bait* recognizer_bait;

   template <typename T,
	     bool _has_generic=has_generic_type<T>::value>
   struct generic_representative {
      typedef typename object_traits<T>::persistent_type type;
   };

   template <typename T>
   struct generic_representative<T, true> {
      typedef typename T::generic_type::persistent_type type;
   };

   // primitive perl types which need special handling if returned by reference in lvalue context
   typedef list primitive_lvalues(bool, int, double, std::string);

   template <typename T>
   struct known_type {
      static const bool value      =sizeof(*recognize(recognizer_bait(0), (T*)0, (T*)0))>=sizeof(SV),
	                exact_match=sizeof(*recognize(recognizer_bait(0), (T*)0, (T*)0))==sizeof(SV4recognize<true>);
   };

   template <typename Base, typename E, typename Params>
   struct known_type< sparse_elem_proxy<Base,E,Params> > {
      static const bool value=known_type<E>::value || list_contains<primitive_lvalues,E>::value,
	                exact_match=false;
   };

   struct type_infos {
      SV *descr, *proto;
      bool allow_magic_storage;
   };

   template <typename T,
	     bool _known=known_type<T>::value, bool _exact_match=known_type<T>::exact_match,
	     bool _has_persistent=known_type<typename object_traits<T>::persistent_type>::value,
	     bool _has_generic=known_type<typename generic_representative<T>::type>::value,
	     bool _is_sparse_proxy=is_instance3_of<T,sparse_elem_proxy>::value>
   class type_cache_helper {
   protected:
      // primary template: nothing known
      // either a special class with prescribed perl package, or a built-in type, or something really undeclared

      static const bool allow_deferred_descr=false;

      static type_infos get()
      {
	 type_infos _infos={ 0, 0, false };
	 if (!list_contains<cons<Object,ObjectType>, T>::value &&
	     (_infos.descr=pm_perl_lookup_cpp_type(typeid(T).name())) ) {
	    _infos.proto=pm_perl_TypeDescr2Proto(_infos.descr);
	    _infos.allow_magic_storage=pm_perl_allow_magic_storage(_infos.proto);
	 }
	 return _infos;
      }

      static type_infos get(SV *prescribed_pkg, bool do_allow_magic_storage)
      {
	 type_infos _infos;
	 _infos.proto=get_type(prescribed_pkg, typeid(T));
	 _infos.allow_magic_storage= do_allow_magic_storage && pm_perl_allow_magic_storage(_infos.proto);
	 _infos.descr= _infos.allow_magic_storage ? polymake::perl_bindings::Class<T>::register_it(0,1,_infos.proto) : 0;
	 return _infos;
      }
   };

   template <typename T>
   class type_cache : protected type_cache_helper<T> {
   protected:
      typedef type_cache_helper<T> super;
      static type_infos& get(type_infos *ti=0)
      {
	 static type_infos _infos=ti ? *ti : super::get();
	 return _infos;
      }
   public:
      static SV* get_descr() { return get().descr; }
      static SV* get_proto() { return get().proto; }
      static SV* provide() { return get_proto(); }	// for ClassRegistrator
      static bool allow_magic_storage(int)
      {
	 return get().allow_magic_storage;
      }
      static bool allow_magic_storage(SV *prescribed_pkg)
      {
	 static type_infos _infos=super::get(prescribed_pkg, !object_traits<T>::is_lazy);
	 return get(&_infos).allow_magic_storage;
      }

      static SV* force_descr()
      {
	 type_infos& infos=get();
	 if (super::allow_deferred_descr && !infos.descr && !infos.allow_magic_storage)
	    // this can happen to semi-builtins like Array<int>
	    infos.descr=pm_perl_Proto2TypeDescr(infos.proto);
	 return infos.descr;
      }

      static wrapper_type get_assignment_operator(SV *src)
      {
	 SV* const descr=get_descr();
	 if (!descr) return 0;
	 return pm_perl_get_assignment_operator(src, descr);
      }

      static wrapper_type get_conversion_operator(SV *src)
      {
	 SV* const descr=get_descr();
	 if (!descr) return 0;
	 return pm_perl_get_conversion_operator(src, descr);
      }

      static wrapper_type get_conversion_constructor(SV *src)
      {
	 SV* const descr=get_descr();
	 if (!descr) return 0;
	 return pm_perl_get_conversion_constructor(src, descr);
      }
   };

   template <typename T, typename Representative>
   class type_cache_via {
   protected:
      static const bool allow_deferred_descr=false;

      static SV* _get_descr(SV *proto, False) { return polymake::perl_bindings::Class<T>::register_it(0,0,proto); }
      static SV* _get_descr(SV*, True) { return 0; }

      static type_infos get()
      {
	 type_infos _infos;
	 _infos.proto=type_cache<Representative>::get_proto();
	 _infos.allow_magic_storage=type_cache<Representative>::allow_magic_storage(0);
	 _infos.descr=_infos.proto ? _get_descr(_infos.proto, bool2type<object_traits<T>::is_lazy>()) : 0;
	 return _infos;
      }

      static type_infos get(SV *prescribed_pkg, bool do_allow_magic_storage)
      {
	 type_infos _infos;
	 _infos.proto=get_type(prescribed_pkg, typeid(T), type_cache<Representative>::get_proto());
	 _infos.allow_magic_storage= do_allow_magic_storage && pm_perl_allow_magic_storage(_infos.proto);
	 _infos.descr= _infos.allow_magic_storage ? polymake::perl_bindings::Class<T>::register_it(0,1,_infos.proto) : 0;
	 return _infos;
      }
   };

   template <typename T, bool _has_generic>
   class type_cache_helper<T, false, false, true, _has_generic, false>
      : public type_cache_via<T, typename object_traits<T>::persistent_type> {};

   template <typename T>
   class type_cache_helper<T, false, false, false, true, false>
      :  public type_cache_via<T, typename generic_representative<T>::type> {};

   template <typename T, bool _has_persistent, bool _has_generic>
   class type_cache_helper<T, true, false, _has_persistent, _has_generic, true> {
   protected:
      static const bool allow_deferred_descr=false;
      static type_infos get()
      {
	 type_infos _infos;
	 _infos.proto=type_cache<typename T::value_type>::get_proto();
	 _infos.allow_magic_storage=true;
	 _infos.descr=polymake::perl_bindings::Class<T>::register_it(0,0,_infos.proto);
	 return _infos;
      }
   };

   template <typename T, bool _has_persistent, bool _has_generic>
   class type_cache_helper<T, true, false, _has_persistent, _has_generic, false> {
   protected:
      static const bool allow_deferred_descr=false;
      static type_infos get()
      {
	 type_infos _infos;
	 _infos.proto=recognize(recognizer_bait(0), (T*)0, (T*)0);
	 _infos.allow_magic_storage=pm_perl_allow_magic_storage(_infos.proto);
	 _infos.descr=polymake::perl_bindings::Class<T>::register_it(0,0,_infos.proto);
	 return _infos;
      }
   };

   template <typename T, bool _has_persistent, bool _has_generic>
   class type_cache_helper<T, true, true, _has_persistent, _has_generic, false> {
   protected:
      static const bool allow_deferred_descr=true;
      static type_infos get()
      {
	 type_infos _infos;
	 _infos.proto=recognize(recognizer_bait(0), (T*)0, (T*)0);
	 _infos.allow_magic_storage=pm_perl_allow_magic_storage(_infos.proto);
	 _infos.descr= _infos.allow_magic_storage ? pm_perl_Proto2TypeDescr(_infos.proto) : 0;
	 return _infos;
      }
   };

   template <typename T, typename Serialized=typename object_traits<T>::serialized>
   struct serialized_of : object_traits<T> {
      typedef Serialized type;
      static const bool obscure=false;
   };

   template <typename T>
   struct serialized_of<T, void> {
      typedef T type;
      static T& serialize(T& x) { return x; }
      static const T& serialize(const T& x) { return x; }
      static const bool obscure=identical<typename object_traits<T>::model, is_container>::value && !has_iterator<T>::value;
   };

   template <typename Top>
   class MaybeUndefined : public Generic<Top> {};

   template <typename Target>
   struct access;
   template <typename Target, bool _try, bool _unwary=Unwary<typename attrib<Target>::minus_const>::value>
   struct access_canned;
   template <typename Target>
   struct check_for_magic_storage;

   class Value : public ScalarBase {
   public:
      explicit Value(value_flags opt_arg=value_trusted) : options(opt_arg) {}

      explicit Value(SV *sv_arg)
	 : ScalarBase(sv_arg), options(value_trusted) {}

      Value(SV *sv_arg, value_flags opt_arg)
	 : ScalarBase(sv_arg), options(opt_arg) {}

      value_flags get_flags() const { return options; }

   protected:
      value_flags options;

      static const char* frame_lower_bound();

      template <typename Numtype> static
      void assign_int(Numtype& x, int i, False, True) { x=i; }

      static void assign_int(int& x, int i, True, True) { x=i; }

      static void assign_int(long& x, int i, True, True) { x=i; }

      template <typename Numtype> static
      void assign_int(Numtype& x, int i, True, True)
      {
	 if (i < std::numeric_limits<Numtype>::min() || i > std::numeric_limits<Numtype>::max())
	    throw std::runtime_error("input integer property out of range");
	 x=static_cast<Numtype>(i);
      }

      template <typename Numtype> static
      void assign_int(Numtype& x, int i, True, False)
      {
	 if (i<0 || static_cast<unsigned int>(i) > std::numeric_limits<Numtype>::max())
	    throw std::runtime_error("input numerical property out of range");
	 x=static_cast<Numtype>(i);
      }

      template <typename Numtype> static
      void assign_float(Numtype& x, double d, False) { x=d; }

      template <typename Numtype> static
      void assign_float(Numtype& x, double d, True)
      {
	 if (d < std::numeric_limits<Numtype>::min() || d > std::numeric_limits<Numtype>::max())
	    throw std::runtime_error("input integer property out of range");
	 x=static_cast<Numtype>(lrint(d));
      }

      template <typename Numtype>
      void num_input(Numtype& x) const
      {
	 switch (pm_perl_number_flags(sv)) {
	 case number_is_int:
	    assign_int(x, pm_perl_int_value(sv), bool2type<std::numeric_limits<Numtype>::is_bounded>(),
		                                 bool2type<std::numeric_limits<Numtype>::is_signed>());
	    break;
	 case number_is_float:
	    assign_float(x, pm_perl_float_value(sv), bool2type<std::numeric_limits<Numtype>::is_bounded>());
	    break;
	 case number_is_object:
	    assign_int(x, pm_perl_object_int_value(sv), bool2type<std::numeric_limits<Numtype>::is_bounded>(),
		                                        bool2type<std::numeric_limits<Numtype>::is_signed>());
	    break;
	 default:
	    if (pm_perl_get_cur_length(sv)==0)
	       x=0;
	    else
	       throw std::runtime_error("invalid value for an input numerical property");
	 }
      }

      False* retrieve(std::string& x) const;
      False* retrieve(const char* &x) const;
      False* retrieve(char &x) const;
      False* retrieve(double& x) const;

      False* retrieve(int& x) const { num_input(x); return 0; }
      False* retrieve(unsigned int& x) const { num_input(x); return 0; }
      False* retrieve(long& x) const { num_input(x); return 0; }
      False* retrieve(unsigned long& x) const { num_input(x); return 0; }
      False* retrieve(bool& x) const { x=pm_perl_is_true(sv); return 0; }
      False* retrieve(float& x) const
      {
	 double xi;
	 retrieve(xi);
	 x=static_cast<float>(xi);
	 return 0;
      }

      False* retrieve(Array& x) const;
      False* retrieve(Object& x) const;
      False* retrieve(ObjectType& x) const;

      template <typename Options, typename Target>
      void do_parse(Target& x) const
      {
	 istream my_stream(sv);
	 PlainParser<Options> parser(my_stream);
	 try {
	    parser >> x;
	    my_stream.finish();
	 } catch (const std::ios::failure&) {
	    std::ostringstream err_pos;
	    err_pos << CharBuffer::get_ptr(my_stream.rdbuf()) - pm_perl_get_string_value_nolen(sv) << '\t';
	    throw std::runtime_error(err_pos.str());
	 }
      }

      // opaque type
      template <typename Target>
      void retrieve(Target& x, False) const
      {
	 if (options & value_not_trusted)
	    ValueInput< TrustedValue<False> >(sv) >> x;
	 else
	    ValueInput<>(sv) >> x;
      }

      // numeric scalar type
      template <typename Target>
      void retrieve(Target& x, True) const
      {
	 this->num_input(x);
      }

      template <typename Target>
      void retrieve_nomagic(Target& x,
			    typename enable_if<int, check_for_magic_storage<Target>::value &&
			                            is_parseable<Target>::value>::type=0) const
      {
	 if (pm_perl_is_plain_text(sv)) {
	    parse(x);
	 } else {
	    if (const char *forbidden_type=pm_perl_get_forbidden_type(sv))
	       throw std::runtime_error("tried to read a full "+std::string(forbidden_type)+" object as an input property");
	    retrieve(x, bool2type<std::numeric_limits<Target>::is_specialized>());
	 }
      }

      template <typename Target>
      void retrieve_nomagic(Target& x,
			    typename enable_if<int, check_for_magic_storage<Target>::value &&
			                            !is_parseable<Target>::value>::type=0) const
      {
	 retrieve(x, False());
      }

      template <typename Target>
      void retrieve_nomagic(Target& x, typename disable_if<int, check_for_magic_storage<Target>::value>::type=0) const
      {
	 retrieve(x);
      }

      template <typename Target>
      True* retrieve(Target& x) const
      {
	 if (!(options & value_ignore_magic)) {
	    if (const typeinfo *t=pm_perl_get_cpp_typeinfo(sv)) {
	       if (*t==typeid(Target)) {
		  if (MaybeWary<Target>::value && (options & value_not_trusted))
		     maybe_wary(x)=*reinterpret_cast<const Target*>(pm_perl_get_cpp_value(sv));
		  else
		     x=*reinterpret_cast<const Target*>(pm_perl_get_cpp_value(sv));
		  return 0;
	       }
	       typedef void (*ass_f)(Target&, const Value&);
	       if (ass_f assignment=reinterpret_cast<ass_f>(type_cache<Target>::get_assignment_operator(sv))) {
		  assignment(x,*this);
		  return 0;
	       }
	    }
	 }
	 retrieve_nomagic(x);
	 return 0;
      }

      template <typename Numtype>
      void num_output(Numtype x)
      {
	 pm_perl_set_int_value(sv,x);
      }

      template <typename Source>
      void store_as_perl(const Source& x)
      {
	 static_cast<ValueOutput<>&>(static_cast<ScalarBase&>(*this)) << x;
	 pm_perl_bless_to_proto(sv, type_cache<typename object_traits<Source>::persistent_type>::get_proto());
      }

      template <typename Stored, typename Source>
      void store(const Source& x)
      {
	 new(pm_perl_new_cpp_value(sv, type_cache<Stored>::get_descr(), options)) Stored(x);
      }

      template <typename Source>
      void store_ref(const Source& x, SV* anchor)
      {
	 pm_perl_share_cpp_value(sv, type_cache<Source>::get_descr(), (void*)&x, anchor, options);
      }

      template <typename Source>
      void store_magic(const Source& x, False, False, False)			// non-persistent regular type
      {
	 typedef typename object_traits<Source>::persistent_type Persistent;
	 if (options & value_allow_non_persistent)
	    store<Source>(x);
	 else
	    store<Persistent>(x);
      }

      template <typename Source, typename _Masquerade, typename _Persistent>
      void store_magic(const Source& x, _Masquerade, True, _Persistent)		// lazy type
      {
	 typedef typename object_traits<Source>::persistent_type Persistent;
	 store<Persistent>(x);
      }

      template <typename Source, typename _Masquerade>
      void store_magic_ref(const Source& x, SV *anchor, _Masquerade, False, False)  // non-persistent regular type
      {
	 typedef typename object_traits<Source>::persistent_type Persistent;
	 if (options & value_allow_non_persistent)
	    store_ref(x,anchor);
	 else
	    store<Persistent>(x);
      }

      template <typename Source, typename _Masquerade, typename _Persistent>
      void store_magic_ref(const Source& x, SV*, _Masquerade, True, _Persistent)	// lazy type
      {
	 typedef typename object_traits<Source>::persistent_type Persistent;
	 store<Persistent>(x);
      }

      template <typename Source>
      void store_magic(const Source& x, False, False, True)			// persistent regular type
      {
	 store<Source>(x);
      }

      template <typename Source>
      void store_magic_ref(const Source& x, SV *anchor, False, False, True)
      {
	 store_ref(x,anchor);
      }

      template <typename Source>
      void store_magic(const Source& x, True, False, False)	// masquerade type belonging to a generic family
      {
	 typedef typename object_traits<Source>::persistent_type Persistent;
	 store<Persistent>(x);
      }

      template <typename Source>
      void store_magic(const Source& x, True, False, True)	// masquerade type without persistent substitute
      {
	 store_as_perl(x);
      }

      template <typename Source>
      void store_magic_ref(const Source& x, SV *anchor, True, False, True)
      {
	 if (options & value_allow_non_persistent)
	    store_ref(x,anchor);
	 else
	    store_as_perl(x);
      }

      void store_primitive_ref(const bool& x, SV *descr, bool take_ref)
      {
	 pm_perl_store_int_lvalue(sv, descr, x, reinterpret_cast<const char*>(take_ref ? &x : 0), options);
      }
      void store_primitive_ref(const int& x, SV *descr, bool take_ref)
      {
	 pm_perl_store_int_lvalue(sv, descr, x, reinterpret_cast<const char*>(take_ref ? &x : 0), options);
      }
      void store_primitive_ref(const double& x, SV *descr, bool take_ref)
      {
	 pm_perl_store_float_lvalue(sv, descr, x, reinterpret_cast<const char*>(take_ref ? &x : 0), options);
      }
      void store_primitive_ref(const std::string& x, SV *descr, bool take_ref)
      {
	 pm_perl_store_string_lvalue(sv, descr, x.c_str(), x.size(), reinterpret_cast<const char*>(take_ref ? &x : 0), options);
      }

   public:
      void put(int x,            SV* =0, const char* =0, int=0) { num_output(x); }
      void put(unsigned int x,   SV* =0, const char* =0, int=0) { num_output(x); }
      void put(long x,           SV* =0, const char* =0, int=0) { num_output(x); }
      void put(unsigned long x,  SV* =0, const char* =0, int=0) { num_output(x); }
      void put(bool x,           SV* =0, const char* =0, int=0) { pm_perl_set_bool_value(sv,x); }
      void put(double x,         SV* =0, const char* =0, int=0) { pm_perl_set_float_value(sv,x); }
      void put(const undefined&, SV* =0, const char* =0, int=0) { pm_perl_set_undef(sv); }

      void put(const char* x,    SV* =0, const char* =0, int=0)
      {
	 if (x)
	    pm_perl_set_string_value_nolen(sv,x);
	 else
	    pm_perl_set_undef(sv);
      }

      template <size_t n>
      void put(const char (&x)[n], SV* =0, const char* =0, int=0)
      {
	 pm_perl_set_string_value(sv,x,n-1);
      }

      void put(const std::string& x, SV* =0, const char* =0, int=0)
      {
	 pm_perl_set_string_value(sv,x.c_str(),x.size());
      }

      void put(char x, SV* =0, const char* =0, int=0)
      {
	 pm_perl_set_string_value(sv,&x,1);
      }

      void put(const Object& x,     SV* =0, const char* =0, int=0);
      void put(const ObjectType& x, SV* =0, const char* =0, int=0);

      void put(const Scalar& x,     SV* =0, const char* =0, int=0);
      void put(const Array& x,      SV* =0, const char* =0, int=0);
      void put(const Hash& x,       SV* =0, const char* =0, int=0);
      void put(const ListReturn& x, SV* =0, const char* =0, int=0);

      template <typename Source, typename PerlPkg>
      void put(const Source& x,
	       SV *arg0,
	       typename enable_if<const char*, serialized_of<Source>::obscure>::type fup,
	       PerlPkg prescribed_pkg)
      {
	 // obscure type (something weird, like a set complement)
	 if (fup && (options & value_allow_non_persistent) &&
	     type_cache<Source>::allow_magic_storage(prescribed_pkg))
	    pm_perl_share_cpp_value(sv, type_cache<Source>::get_descr(), (void*)&x, arg0, options | value_read_only);
	 else
	    throw std::runtime_error("can't store an opaque C++ type without perl binding");
      }

      template <typename Source, typename PerlPkg>
      void put(const Source& x,
	       SV *arg0,
	       typename disable_if<const char*, (serialized_of<Source>::obscure ||
						 derived_from_instance<Source, MaybeUndefined>::value ||
						 is_instance3_of<Source,sparse_elem_proxy>::value ||
						 is_pointer<Source>::value)>::type fup,
	       PerlPkg prescribed_pkg)
      {
	 typedef typename object_traits<Source>::persistent_type Persistent;
	 if (type_cache<Source>::allow_magic_storage(prescribed_pkg)) {

	    if (fup && !object_traits<Source>::is_lazy) {
	       const char *flo=frame_lower_bound(), *val=reinterpret_cast<const char*>(&x);
	       if ((val<flo)==(val<fup)) {
		  // the wrapped function has returned a reference to an object stored elsewhere
		  store_magic_ref(x, arg0, is_masquerade<Source>(), bool2type<object_traits<Source>::is_lazy>(), identical<Source,Persistent>());
		  return;
	       }
	    }
	    // taking reference not allowed or just temporary object
	    store_magic(x, is_masquerade<Source>(), bool2type<object_traits<Source>::is_lazy>(), identical<Source,Persistent>());

	 } else {
	    store_as_perl(x);
	 }
      }

      template <typename Source>
      void put(Source* ptr,
	       SV*,
	       typename enable_if<const char*,
	                          identical<typename object_traits<typename deref<Source>::type>::model, is_opaque>::value>::type=0,
	       int=0)
      {
	 if ((options & value_allow_non_persistent) &&
	     type_cache<Source>::allow_magic_storage(0)) {
	    store_ref(*ptr,0);
	 } else {
	    throw std::runtime_error("can't store an opaque C++ type without perl binding");
	 }
      }

      template <typename Source, typename PerlPkg>
      void put(const MaybeUndefined<Source>& x, SV *arg0, const char *fup, PerlPkg prescribed_pkg)
      {
	 if (x.top().defined()) {
	    put(*x.top(),arg0,fup,prescribed_pkg);
	 } else {
	    put(undefined(),0,0,0);
	 }
      }

      template <typename Base, typename E, typename Params, typename PerlPkg>
      void put(const sparse_elem_proxy<Base,E,Params>& x, SV *arg0, const char*, PerlPkg prescribed_pkg)
      {
	 if ((options & (value_allow_non_persistent | value_expect_lval | value_read_only)) == 
	                (value_allow_non_persistent | value_expect_lval) &&
	     type_cache< sparse_elem_proxy<Base,E,Params> >::allow_magic_storage(prescribed_pkg)) {
	    store< sparse_elem_proxy<Base,E,Params> >(x);
	 } else {
	    put(x.get(),arg0,0,prescribed_pkg);
	 }
      }

      template <typename Source, typename PerlPkg>
      void put_lval(const Source& x, SV *arg0,
		    typename disable_if<const char*, list_contains<primitive_lvalues,Source>::value>::type fup, PerlPkg prescribed_pkg)
      {
	 const typeinfo *arg0_t;
	 if (arg0 && (arg0_t=pm_perl_get_cpp_typeinfo(arg0)) && *arg0_t==typeid(Source) &&
	     reinterpret_cast<const Source*>(pm_perl_get_cpp_value(arg0)) == &x) {
	    forget();
	    sv=arg0;
	 } else {
	    put(x,arg0,fup,prescribed_pkg);
	    if (arg0) get_temp();
	 }
      }

      template <typename Source>
      void put_lval(const Source& x, SV *arg0,
		    typename enable_if<const char*, list_contains<primitive_lvalues,Source>::value>::type fup, int)
      {
	 const char *flo=frame_lower_bound(), *val=reinterpret_cast<const char*>(&x);
	 store_primitive_ref(x, type_cache<Source>::get_descr(), (val<flo)==(val<fup));
	 if (arg0) get_temp();
      }

      void put_lval(const Scalar& x, SV*, const char*, int);
      void put_lval(const Array& x,  SV*, const char*, int);
      void put_lval(const Hash& x,   SV*, const char*, int);

      template <typename Target>
      void parse(Target& x) const
      {
	 if (options & value_not_trusted)
	    do_parse< TrustedValue<False> >(x);
	 else
	    do_parse<void>(x);
      }

      // some code duplication with generic retrieve() is deliberate
      template <typename Target>
      operator Target () const
      {
	 if (check_for_magic_storage<Target>::value) {
	    if (!sv || !pm_perl_is_defined(sv)) {
	       if (options & value_allow_undef) return Target();
	       throw undefined();
	    }
	    if (!(options & value_ignore_magic))
	       if (const typeinfo *t=pm_perl_get_cpp_typeinfo(sv)) {
		  if (*t==typeid(Target))
		     return get_canned<Target>();
		  typedef Target (*conv_f)(const Value&);
		  if (conv_f conversion=reinterpret_cast<conv_f>(type_cache<Target>::get_conversion_operator(sv)))
		     return conversion(*this);
	       }
	    Target x;
	    retrieve_nomagic(x);
	    return x;
	 } else {
	    Target x;
	    // may be overloaded; if not, some specialized retrieve instance will be called
	    *this >> x;
	    return x;
	 }
      }

      operator void* () const { return pm_perl_is_true(sv) ? (void*)1 : 0; }
      bool operator! () const { return !pm_perl_is_true(sv); }

      template <typename Target> friend
      bool operator>> (const Value& me, Target& x)
      {
	 if (!me.sv || !pm_perl_is_defined(me.sv)) {
	    if (!(me.options & value_allow_undef))
	       throw undefined();
	    return false;
	 }
	 me.retrieve(x);
	 return true;
      }

      template <typename Source> friend
      void operator<< (const Value& me, const Source& x)
      {
	 const_cast<Value&>(me).put(x,0,0,0);
      }

      template <typename Target>
      void* allocate()
      {
	 return pm_perl_new_cpp_value(sv, type_cache<Target>::get_descr(), 0);
      }

      template <typename> friend class access;
      template <typename, bool, bool> friend class access_canned;
      template <typename> friend class check_for_magic_storage;

      template <typename Target>
      typename access<Target>::return_type get() const
      {
	 return access<Target>::get(*this);
      }
      using ScalarBase::get;

      // CAUTION: only use after having checked for valid canned magic
      template <typename T>
      T& get_canned() const
      {
	 return *reinterpret_cast<T*>(pm_perl_get_cpp_value(sv));
      }

      template <typename T>
      int lookup_dim(bool tell_size_if_dense)
      {
	 int d=-1;
	 if (pm_perl_is_plain_text(sv)) {
	    istream my_stream(sv);
	    if (options & value_not_trusted)
	       d=PlainParser< TrustedValue<False> >(my_stream).begin_list((T*)0).lookup_dim(tell_size_if_dense);
	    else
	       d=PlainParser<>(my_stream).begin_list((T*)0).lookup_dim(tell_size_if_dense);

	 } else if (pm_perl_get_cpp_typeinfo(sv)) {
	    d=pm_perl_get_cpp_dim(sv, tell_size_if_dense);

	 } else {
	    if (options & value_not_trusted)
	       d=ListValueInput< T, TrustedValue<False> >(sv).lookup_dim(tell_size_if_dense);
	    else
	       d=ListValueInput<T, void>(sv).lookup_dim(tell_size_if_dense);
	 }
	 return d;
      }

      friend class ArrayBase;
      friend class OptionSet;
      template <typename, typename> friend class ListValueInput;
   };

   inline ArrayBase::ArrayBase(const Value& v) : ScalarBase(v.sv)
   {
      if (v.options & value_not_trusted) verify();
   }

   template <typename Target>
   struct access {
      typedef Target type;
      typedef Target return_type;
      typedef typename attrib<Target>::minus_const value_type;

      static return_type get(const Value& v)
      {
	 Target x;
	 v >> x;
	 return x;
      }
   };

   template <typename Target>
   struct access_canned<Target, true, true> {
      typedef Target type;
      typedef Target& return_type;
      typedef typename attrib<Target>::minus_const value_type;

      static return_type get(const Value& v)
      {
	 if (const typeinfo *t=pm_perl_get_cpp_typeinfo(v.sv)) {
	    if (*t==typeid(value_type))
	       return v.get_canned<value_type>();

	    if (wrapper_type conversion=type_cache<value_type>::get_conversion_constructor(v.sv)) {
	       // it is a wrapper for new<T0>(T1), it expects the argument at stack[1]
	       char fup;
	       if (SV *ret=conversion(const_cast<SV**>(&v.sv)-1, &fup))
		  return *reinterpret_cast<value_type*>(pm_perl_get_cpp_value(ret));
	       else
		  throw exception();
	    }
	 }

	 SV *new_mg=pm_perl_newSV();
	 value_type *value=new(pm_perl_new_cpp_value(new_mg, type_cache<value_type>::force_descr(), 0)) value_type;
	 v >> *value;
	 const_cast<Value&>(v).sv=pm_perl_2mortal(new_mg);
	 return *value;
      }
   };

   template <typename Target>
   struct access_canned<Target, false, true> {
      typedef Target type;
      typedef Target& return_type;
      typedef typename attrib<Target>::minus_const value_type;

      static return_type get(const Value& v)
      {
	 return v.get_canned<value_type>();
      }
   };

   template <typename Target, bool _try>
   struct access_canned<Target, _try, false> {
      typedef typename inherit_const<typename Unwary<typename attrib<Target>::minus_const>::type, Target>::type type;
      typedef Target& return_type;

      static return_type get(const Value& v)
      {
	 return wary(access_canned<type, _try, true>::get(v));
      }
   };

   template <typename Target>
   struct access< TryCanned<Target> > : access_canned<Target, true> {};

   template <typename Target>
   struct access< Canned<Target> > : access_canned<Target, false> {};

   template <typename Target>
   struct access< Enum<Target> > {
      typedef const Target type;
      typedef Target return_type;
      typedef Target value_type;

      static return_type get(const Value& v)
      {
	 return Target(pm_perl_int_value(pm_perl_deref(v.sv)));
      }
   };

   template <typename Target>
   struct check_for_magic_storage {
      struct helper {
	 static derivation::yes Test(True*);
	 static derivation::no Test(False*);
	 static Target& piece();
      };
      static const bool value= sizeof(helper::Test(Value().retrieve(helper::piece()))) == sizeof(derivation::yes);
   };

   template <typename ElementType, typename Options>
   template <typename T> inline
   ListValueInput<ElementType, Options>&
   ListValueInput<ElementType, Options>::operator>> (T& x)
   {
      if (_opts::template get_option< CheckEOF<False> >::value && at_end())
	 throw std::runtime_error("list input - size mismatch");
      Value elem((*this)[i++], _opts::template get_option< TrustedValue<True> >::value ? value_trusted : value_not_trusted );
      elem >> x;
      return *this;
   }

   template <typename ElementType, typename Options>
   template <typename T> inline
   int ListValueInput<ElementType, Options>::lookup_lower_dim(bool tell_size_if_dense)
   {
      if (_size==0) return 0;
      Value first_elem((*this)[0], _opts::template get_option< TrustedValue<True> >::value ? value_trusted : value_not_trusted);
      return first_elem.lookup_dim<T>(tell_size_if_dense);
   }

   template <typename T> inline
   ListReturn& ListReturn::operator<< (const T& x)
   {
      Value elem;
      elem << x;
      push(elem);
      finish();
      return *this;
   }

   class Array_access {
   protected:
      SV *sv;
   public:
      Array_access(SV *arg=0) : sv(arg) {}

      typedef int argument_type;
      typedef Value result_type;
      Value operator() (int i) const
      {
	 return Value(*pm_perl_AV_fetch(sv,i), value_not_trusted);
      }
   };

   class Array : public ArrayBase,
		 public modified_container_impl<Array,
						list ( Container< sequence >,
						       Operation< Array_access > ) > {
   public:
      Array() {}
      explicit Array(int n) { resize(n); }
      Array(const Array& x) { pm_perl_set_copy(sv,x.sv); }
      Array& operator= (const Array& x) { pm_perl_set_copy(sv,x.sv); return *this; }
      ~Array() { forget(); }

      using ArrayBase::size;
      using modified_container_impl<Array>::operator[];

      bool empty() const { return size()==0; }

      sequence get_container() const { return sequence(0,size()); }
      Array_access get_operation() const { return sv; }

      void resize(int n) { pm_perl_AV_resize(sv,n); }
      void clear() { resize(0); }

      friend class Value;
   };

   class OptionSet : public HashBase {
   public:
      OptionSet(const Value& v)
	 : HashBase(v.sv) { verify(); }
   protected:
      Value _get(const char* key, size_t klen) const
      {
	 SV *elem=pm_perl_HV_fetch(sv,key,klen,false);
	 return Value(elem ? elem : pm_perl_undef(), value_flags(value_not_trusted | value_allow_undef));
      }
   public:
      template <size_t klen>
      Value operator[] (const char (&key)[klen]) const { return _get(key, klen-1); }
      Value operator[] (const std::string& key) const { return _get(key.c_str(), key.size()); }
   };

   class Hash : public HashBase {
   public:
      Hash() {}
      Hash(const Hash& x) { pm_perl_set_copy(sv,x.sv); }
      Hash& operator= (const Hash& x) { pm_perl_set_copy(sv,x.sv); return *this; }
      ~Hash() { forget(); }
   protected:
      Value _get(const char* key, size_t klen) const
      {
	 SV *elem=pm_perl_HV_fetch(sv,key,klen,false);
	 return Value(elem ? elem : pm_perl_undef(), value_flags(value_not_trusted | value_allow_undef));
      }

      Value _set(const char* key, size_t klen) const
      {
	 return Value(pm_perl_HV_fetch(sv,key,klen,true), value_flags(value_not_trusted | value_allow_undef));
      }
   public:
      template <size_t klen>
      Value operator[] (const char (&key)[klen]) const { return _get(key, klen-1); }
      Value operator[] (const std::string& key) const { return _get(key.c_str(), key.size()); }

      template <size_t klen>
      Value operator[] (const char (&key)[klen]) { return _set(key, klen-1); }
      Value operator[] (const std::string& key) { return _set(key.c_str(), key.size()); }
   };

   inline void Value::put(const Scalar& x,      SV*, const char*, int) { pm_perl_set_copy(sv, x.get()); }
   inline void Value::put(const Array& x,       SV*, const char*, int) { pm_perl_set_copy(sv, x.get()); }
   inline void Value::put(const Hash& x,        SV*, const char*, int) { pm_perl_set_copy(sv, x.get()); }
   inline void Value::put(const ListReturn& x,  SV*, const char*, int) { forget(); sv=0; x.finish(); }
   inline void Value::put_lval(const Scalar& x, SV*, const char*, int) { forget(); sv=x.get(); }
   inline void Value::put_lval(const Array& x,  SV*, const char*, int) { forget(); sv=x.get(); }
   inline void Value::put_lval(const Hash& x,   SV*, const char*, int) { forget(); sv=x.get(); }
} }

#endif // _POLYMAKE_PERL_VALUE_H

// Local Variables:
// mode:C++
// End:
