#include #include #include #include #include #include #include #include #include #include namespace saw { namespace binding { /** * The synchronous simple interface */ struct SyncC {}; } /** * Helper to determine if we are dealing with primitive types */ template struct c_is_primitive { static constexpr bool value = false; }; template struct c_is_primitive> { static constexpr bool value = true; }; template struct c_primitive_string { static_assert(always_false, "Not supported"); }; template<> struct c_primitive_string { static constexpr string_literal name = "int8"; static constexpr string_literal value = "int8_t"; }; template<> struct c_primitive_string { static constexpr string_literal name = "int16"; static constexpr string_literal value = "int16_t"; }; template<> struct c_primitive_string { static constexpr string_literal name = "int32"; static constexpr string_literal value = "int32_t"; }; template<> struct c_primitive_string { static constexpr string_literal name = "int64"; static constexpr string_literal value = "int64_t"; }; template<> struct c_primitive_string { static constexpr string_literal name = "uint8"; static constexpr string_literal value = "uint8_t"; }; template<> struct c_primitive_string { static constexpr string_literal name = "uint16"; static constexpr string_literal value = "uint16_t"; }; template<> struct c_primitive_string { static constexpr string_literal name = "uint32"; static constexpr string_literal value = "uint32_t"; }; template<> struct c_primitive_string { static constexpr string_literal name = "uint64"; static constexpr string_literal value = "uint64_t"; }; template<> struct c_primitive_string { static constexpr string_literal name = "float32"; static constexpr string_literal value = "float"; }; template<> struct c_primitive_string { static constexpr string_literal name = "float64"; static constexpr string_literal value = "double"; }; struct language_binding_config { std::string prefix; }; namespace impl { template struct lang_bind { static_assert(always_false, "Not supported"); }; struct language_binding_state { struct info { std::string type; }; std::map hashes; }; struct lang_bind_helper { static error_or append_string(buffer& buff, const std::string_view& str){ for(uint64_t i = 0; i < str.size(); ++i){ auto err = buff.push(static_cast(str[i])); if(!err.template is_type()){ return err; } } return void_t{}; } static error_or append_hashed_type(buffer& buff, const std::string_view& prefix, uint32_t hash){ { auto eov = append_string(buff, prefix); if(eov.is_error()){ return eov; } } { auto eov = append_string(buff, "_"); if(eov.is_error()){ return eov; } } { auto eov = append_string(buff, std::to_string(hash)); if(eov.is_error()){ return eov; } } { auto eov = append_string(buff, "_t"); if(eov.is_error()){ return eov; } } return void_t{}; } }; template struct lang_bind, binding::SyncC> { using Schema = schema::Primitive; static error_or generate_c_to_cpp(buffer& src, const language_binding_config& cfg, language_binding_state& state, const std::string_view& cpp_name, const std::string_view& c_name ){ { auto eov = lang_bind_helper::append_string(src, "\t"); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(src, cpp_name); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(src, ".set(*"); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(src, c_name); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(src, ");\n"); if(eov.is_error()){ return eov; } } return void_t{}; } static error_or generate_cpp_to_c(buffer& src, const language_binding_config& cfg, language_binding_state& state, const std::string_view& cpp_name, const std::string_view& c_name ){ { auto eov = lang_bind_helper::append_string(src, "\t*"); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(src, c_name); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(src, " = "); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(src, cpp_name); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(src, ".get();\n"); if(eov.is_error()){ return eov; } } return void_t{}; } static error_or generate(buffer& buff, buffer& src, const language_binding_config& cfg, language_binding_state& state){ constexpr uint64_t hash = schema_hash::apply(); { std::string hash_type_str = cfg.prefix + "_" + std::to_string(hash) + "_t"; auto emp = state.hashes.emplace(std::make_pair(hash, hash_type_str)); if(emp.second){ { auto eov = lang_bind_helper::append_string(buff, "typedef "); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, c_primitive_string::value.view()); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, " "); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, hash_type_str); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, ";\n"); if(eov.is_error()){ return eov; } } } } return void_t{}; } }; template struct lang_bind, binding::SyncC> { using Schema = schema::Array; static_assert(is_primitive::value, "Currently only primitive type arrays are supported"); static error_or generate_c_to_cpp(buffer& src, const language_binding_config& cfg, language_binding_state& state, const std::string_view& cpp_name, const std::string_view& c_name ){ return make_error(); } static error_or generate_cpp_to_c(buffer& src, const language_binding_config& cfg, language_binding_state& state, const std::string_view& c_name, const std::string_view& cpp_name ){ return make_error(); } static error_or generate(buffer& buff, buffer& src, const language_binding_config& cfg, language_binding_state& state){ constexpr uint64_t hash = schema_hash::apply(); std::string hash_type_str = cfg.prefix + "_" + std::to_string(hash) + "_t"; auto emp = state.hashes.emplace(std::make_pair(hash, hash_type_str)); if(emp.second){ { auto eov = lang_bind::generate(buff, src, cfg, state); if(eov.is_error()){ return eov; } } { auto eov = lang_bind::generate(buff, src, cfg, state); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, "struct "); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_hashed_type(buff, cfg.prefix, hash); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, " {\n\t"); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_hashed_type(buff, cfg.prefix, schema_hash::apply()); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, "* data;\n\t"); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_hashed_type(buff, cfg.prefix, schema_hash::apply()); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, " length;\n};\n"); if(eov.is_error()){ return eov; } } } return void_t{}; } }; template struct lang_bind, binding::SyncC> { static error_or append_function_def(buffer& buff, const language_binding_config& cfg, language_binding_state& state, const std::string_view& f_name){ /** * Generate the base function declaration */ { auto eov = lang_bind_helper::append_string(buff, "int "); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, cfg.prefix); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, "_"); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, f_name); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, " ( "); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, cfg.prefix); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, "_context* ctx, const "); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, cfg.prefix); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, "_"); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, f_name); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, "_input_t* input, "); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, cfg.prefix); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, "_"); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, f_name); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, "_output_t* output )"); if(eov.is_error()){ return eov; } } return void_t{}; } static error_or generate(buffer& buff, buffer& src, const language_binding_config& cfg, language_binding_state& state, const std::string_view& f_name){ constexpr uint64_t input_hash = schema_hash::apply(); constexpr uint64_t output_hash = schema_hash::apply(); /** * Source */ { auto eov = append_function_def(src, cfg, state, f_name); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(src, "{\n\tif(!ctx || !(ctx->ctx)) return -1;\n\tif(!input) return -1;\n\tif(!output) return -1;\n"); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(src, "\n\tauto& iface = "); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(src, cfg.prefix); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(src, "_get_cpp_interface_priv(*ctx);\n\t"); if(eov.is_error()){ return eov; } } /** * Check declarations and definitions. * Translate input. */ { auto eov = lang_bind_helper::append_string(src, "saw::data<"); if(eov.is_error()){ return eov; } } { std::stringstream ss; try{ { schema_stringify::apply(ss); auto eov = lang_bind_helper::append_string(src, ss.str()); if(eov.is_error()){ return eov; } } }catch(const std::exception&){ return make_error(); } } { auto eov = lang_bind_helper::append_string(src, "> cpp_input_root;\n"); if(eov.is_error()){ return eov; } } { auto eov = lang_bind::generate(buff, src, cfg, state); if(eov.is_error()){ return eov; } } { auto eov = lang_bind::generate_c_to_cpp(src, cfg, state, "c_input_root", "cpp_input_root"); if(eov.is_error()){ return eov; } } /** * Call the c++ implementation */ { auto eov = lang_bind_helper::append_string(src, "\n\tauto eov = iface.template call<\""); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(src, f_name); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(src, "\">(std::move(cpp_input_root));\n"); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(src, "\tif(eov.is_error()){\n\t\tauto& err = eov.get_error();\n\t\treturn err.get_code();\n\t}\n\tauto& cpp_output_root = eov.get_value();\n\n"); if(eov.is_error()){ return eov; } } { auto eov = lang_bind::generate_cpp_to_c(src, cfg, state, "cpp_output_root", "c_output_root"); if(eov.is_error()){ return eov; } } /** * Check declarations and definitions. * Translate output. */ { auto eov = lang_bind::generate(buff, src, cfg, state); if(eov.is_error()){ return eov; } } /** * End source declaration */ { auto eov = lang_bind_helper::append_string(src, "\treturn 0;\n}\n\n"); if(eov.is_error()){ return eov; } } /** * Header */ { auto eov = lang_bind_helper::append_string(buff, "typedef "); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_hashed_type(buff, cfg.prefix, schema_hash::apply()); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, " "); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, cfg.prefix); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, "_"); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, f_name); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, "_input_t;\n"); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, "typedef "); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_hashed_type(buff, cfg.prefix, schema_hash::apply()); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, " "); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, cfg.prefix); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, "_"); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, f_name); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, "_output_t;\n"); if(eov.is_error()){ return eov; } } /** * Function generations */ { auto eov = append_function_def(buff, cfg, state, f_name); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, ";\n\n"); if(eov.is_error()){ return eov; } } return void_t{}; } }; template struct lang_bind, binding::SyncC> { template static error_or generate_element(buffer& buff, buffer& src, const language_binding_config& cfg, language_binding_state& state){ using Member = typename parameter_pack_type::type; using MValue = typename Member::ValueType; static constexpr string_literal MKey = Member::KeyLiteral; { auto eov = lang_bind::generate(buff, src, cfg, state, MKey.view()); if(eov.is_error()){ return eov; } } if constexpr ((i+1) < sizeof...(M) ){ return generate_element(buff, src, cfg, state); } return void_t{}; } static error_or generate(buffer& buff, buffer& src, const language_binding_config& cfg){ if(cfg.prefix.size() == 0){ return make_error("C interfaces need a prefix."); } language_binding_state state; /** * Setup context creation to bind interface to a context */ /** * Interface type helper */ { auto eov = lang_bind_helper::append_string(src, "namespace {\nnamespace schema {\n"); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(src, "using InterfaceType = "); if(eov.is_error()){ return eov; } } { std::stringstream ss; schema_stringify>::apply(ss); try { auto eov = lang_bind_helper::append_string(src, ss.str()); if(eov.is_error()){ return eov; } }catch(const std::exception&){ return make_error("No memory after stringification"); } } { auto eov = lang_bind_helper::append_string(src, ";\n}\n}\n\n"); if(eov.is_error()){ return eov; } } /** * Context definition */ /// @todo Add Macro which reduces typing time ? Not really that much shorter :/ // SAW_CALL_AND_RETURN_ON_ERROR(lang_bind_helper::append_string(buff, "struct ")); { auto eov = lang_bind_helper::append_string(buff, "struct "); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, cfg.prefix); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, "_context {\n\tvoid* ctx;\n};\n\n"); if(eov.is_error()){ return eov; } } /** * Create and destroy declaration */ auto ctx_func = [&](std::string_view str) -> error_or{ { auto eov = lang_bind_helper::append_string(buff, "int "); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, cfg.prefix); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, "_context_"); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, str); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, " ( "); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, cfg.prefix); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(buff, "_context* ctx );\n\n"); if(eov.is_error()){ return eov; } } return void_t{}; }; { auto eov = ctx_func("create"); if(eov.is_error()){ return eov; } } { auto eov = ctx_func("destroy"); if(eov.is_error()){ return eov; } } /** * Create a casting helper in sources for the interface type */ { auto eov = lang_bind_helper::append_string(src, "saw::interface& "); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(src, cfg.prefix); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(src, "_get_cpp_interface_priv("); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(src, cfg.prefix); if(eov.is_error()){ return eov; } } { auto eov = lang_bind_helper::append_string(src, "& ctx){\n\treturn *reinterpret_cast*>(ctx.ctx);\n}\n\n"); if(eov.is_error()){ return eov; } } if constexpr (sizeof...(M) > 0){ return generate_element<0>(buff, src, cfg, state); } return void_t{}; } }; } template struct language_binding { static_assert(always_false, "Case not supported."); }; template struct language_binding { static error_or generate(buffer& buff, buffer& src, const language_binding_config& cfg){ return impl::lang_bind::generate(buff, src, cfg); } }; }