-
Mark Winter authoredMark Winter authored
ArgConverter.h 9.68 KiB
#pragma once
#include "mph/const_string.h"
#include "ScriptTraits.h"
#include "ScriptTraitTfms.h"
namespace Script
{
// Derivation of types for script/concrete variable tuples
template <typename Tuple>
using script_transform = mph::tuple_type_tfm<iotrait_to_script, Tuple>;
template <typename Tuple>
using concrete_transform = mph::tuple_type_tfm<iotrait_to_concrete, Tuple>;
template <typename OutT, typename InT, typename Tuple>
using deferred_concrete_transform = mph::tuple_type_tfm<deferred_to_concrete<OutT,InT>::template tfm, Tuple>;
/////////////
// ArgConverter - Base structure for script-engine to c++ argument conversions (used by ScriptCommands)
// NOTE: These types are generated automatically by GenCommands.h and have fixed argument layouts
template <typename Derived, typename... Layout>
struct ArgConverter
{
protected:
using ArgConvertError = typename Script::Converter::ArgConvertError;
// General argument error exception
class ArgError: public RuntimeError
{
static std::string make_convert_msg(const ArgConvertError& ace)
{
return std::string(ace.getArgName()) + ": " + ace.what();
}
public:
ArgError() = delete;
template <typename... Args>
ArgError(const char* fmt, Args&&... args)
: RuntimeError(fmt, std::forward<Args>(args)...)
{}
ArgError(const ArgConvertError& ace): ArgError(make_convert_msg(ace).c_str())
{}
};
public:
// Argument type layout alias (e.g. std::tuple<OutParam<Image<Deferred>>,...>)
using ArgLayout = std::tuple<Layout...>;
// Script argument type layout (e.g. std::tuple<const PyArrayObject*,...>
using ScriptTypes = typename script_transform<std::tuple<Layout...>>::type;
using ScriptPtrs = typename mph::tuple_ptr_t<ScriptTypes>;
// Concrete type layouts (e.g. std::tuple<PyObject*,...>)
using ArgTypes = typename concrete_transform<std::tuple<Layout...>>::type;
using ArgPtrs = typename mph::tuple_ptr_t<ArgTypes>;
// Templated concrete deferred type layout
template <typename OutT, typename InT>
using ConcreteArgTypes = typename deferred_concrete_transform<OutT,InT,ArgLayout>::type;
// Compositeable type selectors
template <typename... Chain> using S_Arg = compose_selector<ArgLayout, true_pred, Chain...>;
template <typename... Chain> using S_Out = compose_selector<ArgLayout, is_outparam, Chain...>;
template <typename... Chain> using S_In = compose_selector<ArgLayout, is_inparam, Chain...>;
template <typename... Chain> using S_Opt = compose_selector<ArgLayout, is_optparam, Chain...>;
template <typename... Chain> using S_InOpt = S_Arg<S_In<Chain...>, S_Opt<Chain...>>;
template <typename... Chain> using S_Image = compose_selector<ArgLayout, is_image, Chain...>;
template <typename... Chain> using S_Defer = compose_selector<ArgLayout, is_deferred, Chain...>;
template <typename... Chain> using S_Nondef = compose_selector<ArgLayout, not_deferred, Chain...>;
// Specific composites selector types
using OutputSel = typename S_Out<>::selector;
using InputSel = typename S_In<>::selector;
using OptionalSel = typename S_Opt<>::selector;
using DeferredSel = typename S_Defer<>::selector;
using DeferredInOptSel = typename S_Defer<S_InOpt<>>::selector;
using DeferredInImSel = typename S_Defer<S_In<S_Image<>>>::selector;
using DeferredOutSel = typename S_Defer<S_Out<>>::selector;
using DeferredOutImSel = typename S_Defer<S_Out<S_Image<>>>::selector;
using NondeferredSel = typename S_Nondef<>::selector;
using NondeferOutSel = typename S_Nondef<S_Out<>>::selector;
using NondeferInOptSel = typename S_Nondef<S_InOpt<>>::selector;
// Argument layout subsets
using OutLayout = typename OutputSel::template type<ArgLayout>;
using InLayout = typename InputSel::template type<ArgLayout>;
using OptLayout = typename OptionalSel::template type<ArgLayout>;
// IO-type stripped layout subsets (e.g. OutParam<Image<Deferred>> -> Image<Deferred>)
using OutTypeLayout = typename mph::tuple_type_tfm<strip_outer, OutLayout>::type;
using InTypeLayout = typename mph::tuple_type_tfm<strip_outer, InLayout>::type;
using OptTypeLayout = typename mph::tuple_type_tfm<strip_outer, OptLayout>::type;
// Optional argument pointers (used for setting defaults)
using OptPtrs = typename OptionalSel::template type<ArgPtrs>;
public:
inline static std::string outargstr()
{
return argstr<OutputSel, BracketNone>();
}
inline static std::string inoptargstr()
{
if ( InputSel::seq::size() > 0 && OptionalSel::seq::size() > 0 )
return argstr<InputSel, BracketNone>() + "," + argstr<OptionalSel, BracketSquare>();
if ( OptionalSel::seq::size() > 0 )
return argstr<OptionalSel, BracketSquare>();
else
return argstr<InputSel,BracketNone>();
}
private:
struct BracketNone
{
template<std::size_t N>
static constexpr auto bracketArg(const mph::const_string<N>& str)
-> const mph::const_string<N>&
{
return str;
}
};
struct BracketSquare
{
template<std::size_t N>
static constexpr auto bracketArg(const mph::const_string<N>& str)
-> mph::const_string<N+2>
{
return "[" + str + "]";
}
};
inline std::string comma_delim(const std::string& strA, const std::string& strB)
{
if (strA.empty() || strB.empty())
return strA + strB;
else
return strA + "," + strB;
}
template <typename ArgSelector, typename BracketType>
inline static std::string argstr()
{
return argstr_impl<BracketType>(typename ArgSelector::seq{});
}
template <typename BracketType, std::size_t... Is>
inline static std::string argstr_impl(mph::index_sequence<Is...>)
{
using Seq = mph::index_sequence<Is...>;
const std::size_t seq_size = Seq::size();
using CSeq = typename mph::split_sequence<seq_size-1, Seq>::left;
using Last = typename mph::split_sequence<seq_size-1, Seq>::right;
return argstr_cat<BracketType>(CSeq(), Last());
}
template <typename BracketType>
inline static std::string argstr_impl(mph::index_sequence<>)
{
return "";
}
template <typename BrackeType, std::size_t... Is, std::size_t Il>
inline static std::string argstr_cat(mph::index_sequence<Is...>, mph::index_sequence<Il>)
{
// TODO: Bubble constexpr upward if we switch to C++14
return mph::const_strcat(
(BrackeType::bracketArg(std::get<Is>(Derived::argNames)) + ",")...,
BrackeType::bracketArg(std::get<Il>(Derived::argNames)));
}
public:
static constexpr bool has_deferred_image_inputs() noexcept
{
return (DeferredInImSel::seq::size() > 0);
}
static constexpr bool has_deferred_image_outputs() noexcept
{
return (DeferredOutImSel::seq::size() > 0);
}
template <typename... Args>
static IdType getInputType(Args&&... ioargs)
{
// TODO: Stop this from erroring if no deferred inputs
auto in_defer_tuple = DeferredInImSel::select(std::tuple<Args...>(std::forward<Args>(ioargs)...));
return Script::ArrayInfo::getType(std::get<0>(in_defer_tuple));
}
template <typename... Args>
static DimInfo getInputDimInfo(const std::tuple<Args...>& argtuple)
{
auto in_defer_tuple = DeferredInImSel::select(argtuple);
return Script::getDimInfo(std::get<0>(in_defer_tuple));
}
static void setOptionalDefaults(ArgPtrs argPtrs)
{
Derived::setOptional(OptionalSel::select(argPtrs));
}
template <typename OutPtrs, typename InPtrs, typename Selector>
static void convertSelected(OutPtrs outPtrs, InPtrs inPtrs, Selector)
{
// TODO: Potentially pre-check for conversion compatibility
// Converters to pass script args to actual non-deferred input types
convert_arg_subset(outPtrs, inPtrs, typename Selector::seq{});
}
template <typename ConcretePtrs, typename ScrPtrs>
static void createOutImRefs(ConcretePtrs cncPtrs, ScrPtrs scrPtrs, const DimInfo& info)
{
// TODO: Change image selector (or add new one to make sure this is only for ImageRefs)
// TODO: Also add static checks since output image owners unsupported
using BaseTypes = base_data_type_t<ConcretePtrs>;
mph::tuple_deref(DeferredOutImSel::select(scrPtrs)) = create_arrays<BaseTypes>(info, typename DeferredOutImSel::seq{});
convert_arg_subset(cncPtrs, scrPtrs, typename DeferredOutImSel::seq{});
}
protected:
// Converting input arguments (script types are pointers)
template <typename OutT, typename InT>
static void convert_arg(OutT& out, const InT* inPtr, const char* argName)
{
// NOTE: if inPtr is nullptr then this is presumed to be optional
if ( inPtr == nullptr )
return;
try
{
Derived::convert_impl(out, inPtr);
}
catch ( ArgConvertError& ace )
{
ace.setArgName(argName);
throw;
}
}
// Convert output arguments
template <typename OutT, typename InT>
static void convert_arg(OutT*& outPtr, const InT& in, const char* argName)
{
if ( outPtr == nullptr )
throw ArgConvertError(argName, "Output parameter cannot be null");
try
{
Derived::convert_impl(outPtr, in);
}
catch ( ArgConvertError& ace )
{
ace.setArgName(argName);
throw;
}
}
template <typename... Targets, typename... Args, size_t... Is>
static void convert_arg_subset(std::tuple<Targets*...> targets, std::tuple<Args*...> args, mph::index_sequence<Is...>)
{
try
{
(void)std::initializer_list<int>
{
(convert_arg((*std::get<Is>(targets)), (*std::get<Is>(args)), std::get<Is>(Derived::argNames).c_str()), void(), 0)...
};
}
catch ( ArgConvertError& ace )
{
throw ArgError(ace);
}
}
template <typename TargetTuple, size_t... Is>
static auto create_arrays(const DimInfo& info, mph::index_sequence<Is...>)
-> decltype(std::make_tuple(createArray<mph::tuple_select_t<Is, TargetTuple>>(std::declval<const DimInfo&>())...))
{
return std::make_tuple(createArray<mph::tuple_select_t<Is,TargetTuple>>(info)...);
}
};
};