bond 0.9.0.0 → 0.10.0.0
raw patch · 14 files changed
+589/−224 lines, 14 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Language.Bond.Codegen.Templates: grpc_h :: Maybe String -> MappingContext -> String -> [Import] -> [Declaration] -> (String, Text)
+ Language.Bond.Syntax.Types: [serviceBase] :: Declaration -> Maybe Type
- Language.Bond.Syntax.Types: Service :: [Namespace] -> [Attribute] -> String -> [TypeParam] -> [Method] -> Declaration
+ Language.Bond.Syntax.Types: Service :: [Namespace] -> [Attribute] -> String -> [TypeParam] -> Maybe Type -> [Method] -> Declaration
Files
- Main.hs +34/−18
- Options.hs +10/−0
- bond.cabal +6/−3
- src/Language/Bond/Codegen/Cpp/ApplyOverloads.hs +28/−20
- src/Language/Bond/Codegen/Cpp/Apply_cpp.hs +6/−10
- src/Language/Bond/Codegen/Cpp/Apply_h.hs +6/−7
- src/Language/Bond/Codegen/Cpp/Grpc_h.hs +291/−0
- src/Language/Bond/Codegen/Cpp/Reflection_h.hs +2/−2
- src/Language/Bond/Codegen/Templates.hs +2/−0
- src/Language/Bond/Parser.hs +11/−1
- src/Language/Bond/Syntax/Types.hs +1/−0
- tests/Main.hs +0/−163
- tests/TestMain.hs +180/−0
- tests/Tests/Codegen.hs +12/−0
Main.hs view
@@ -6,7 +6,9 @@ import System.Environment (getArgs, withArgs) import System.Directory import System.FilePath +import Data.Maybe import Data.Monoid +import qualified Data.Foldable as F import Control.Monad import Prelude import Control.Concurrent.Async @@ -29,7 +31,7 @@ main :: IO() main = do args <- getArgs - options <- (if null args then withArgs ["--help"] else id) getOptions + options <- (if null args then withArgs ["--help=all"] else id) getOptions setJobs $ jobs options case options of Cpp {..} -> cppCodegen options @@ -69,28 +71,32 @@ cppCodegen :: Options -> IO() cppCodegen options@Cpp {..} = do let typeMapping = maybe cppTypeMapping cppCustomAllocTypeMapping allocator - concurrentlyFor_ files $ codeGen options typeMapping $ - [ reflection_h export_attribute - , types_h header enum_header allocator - , types_cpp - , apply_h applyProto export_attribute - , apply_cpp applyProto - , comm_h export_attribute - , comm_cpp - ] <> - [ enum_h | enum_header] + concurrentlyFor_ files $ codeGen options typeMapping templates where applyProto = map snd $ filter (enabled apply) protocols enabled a p = null a || fst p `elem` a protocols = [ (Compact, ProtocolReader " ::bond::CompactBinaryReader< ::bond::InputBuffer>") , (Compact, ProtocolWriter " ::bond::CompactBinaryWriter< ::bond::OutputBuffer>") - , (Compact, ProtocolWriter " ::bond::CompactBinaryWriter< ::bond::OutputCounter>") + , (Compact, ProtocolWriter " ::bond::CompactBinaryWriter< ::bond::CompactBinaryCounter::type>") , (Fast, ProtocolReader " ::bond::FastBinaryReader< ::bond::InputBuffer>") , (Fast, ProtocolWriter " ::bond::FastBinaryWriter< ::bond::OutputBuffer>") , (Simple, ProtocolReader " ::bond::SimpleBinaryReader< ::bond::InputBuffer>") , (Simple, ProtocolWriter " ::bond::SimpleBinaryWriter< ::bond::OutputBuffer>") ] + templates = concat $ map snd $ filter fst codegen_templates + codegen_templates = [ (core_enabled, core_files) + , (comm_enabled, [comm_h export_attribute, comm_cpp]) + , (grpc_enabled, [grpc_h export_attribute]) + ] + core_files = [ + reflection_h export_attribute + , types_h header enum_header allocator + , types_cpp + , apply_h applyProto export_attribute + , apply_cpp applyProto + ] <> + [ enum_h | enum_header] cppCodegen _ = error "cppCodegen: impossible happened." csCodegen :: Options -> IO() @@ -112,6 +118,12 @@ ] csCodegen _ = error "csCodegen: impossible happened." +anyServiceInheritance :: [Declaration] -> Bool +anyServiceInheritance = getAny . F.foldMap serviceWithBase + where + serviceWithBase Service{..} = Any $ isJust serviceBase + serviceWithBase _ = Any False + codeGen :: Options -> TypeMapping -> [Template] -> FilePath -> IO () codeGen options typeMapping templates file = do let outputDir = output_dir options @@ -120,9 +132,13 @@ namespaceMapping <- parseNamespaceMappings $ namespace options (Bond imports namespaces declarations) <- parseFile (import_dir options) file let mappingContext = MappingContext typeMapping aliasMapping namespaceMapping namespaces - forM_ templates $ \template -> do - let (suffix, code) = template mappingContext baseName imports declarations - let fileName = baseName ++ suffix - createDirectoryIfMissing True outputDir - let content = if (no_banner options) then code else (commonHeader "//" fileName <> code) - L.writeFile (outputDir </> fileName) content + case (anyServiceInheritance declarations, service_inheritance_enabled options, grpc_enabled options, comm_enabled options) of + (True, False, _, _) -> fail "Use --enable-service-inheritance to enable service inheritance syntax." + (True, True, True, _) -> fail "Service inheritance is not supported in gRPC codegen." + (True, True, _, True) -> fail "Service inheritance is not supported in Comm codegen." + _ -> forM_ templates $ \template -> do + let (suffix, code) = template mappingContext baseName imports declarations + let fileName = baseName ++ suffix + createDirectoryIfMissing True outputDir + let content = if (no_banner options) then code else (commonHeader "//" fileName <> code) + L.writeFile (outputDir </> fileName) content
Options.hs view
@@ -38,6 +38,10 @@ , export_attribute :: Maybe String , jobs :: Maybe Int , no_banner :: Bool + , core_enabled :: Bool + , comm_enabled :: Bool + , grpc_enabled :: Bool + , service_inheritance_enabled :: Bool } | Cs { files :: [FilePath] @@ -53,6 +57,7 @@ , structs_enabled :: Bool , comm_enabled :: Bool , grpc_enabled :: Bool + , service_inheritance_enabled :: Bool } | Schema { files :: [FilePath] @@ -60,6 +65,7 @@ , output_dir :: FilePath , jobs :: Maybe Int , runtime_schema :: Bool + , service_inheritance_enabled :: Bool } deriving (Show, Data, Typeable) @@ -77,6 +83,10 @@ , export_attribute = def &= typ "ATTRIBUTE" &= explicit &= name "apply-attribute" &= name "export-attribute" &= help "Prefix declarations for library export with the specified C++ attribute/declspec. apply-attribute is a deprecated synonym." , jobs = def &= opt "0" &= typ "NUM" &= name "j" &= help "Run NUM jobs simultaneously (or '$ncpus' if no NUM is not given)" , no_banner = def &= help "Omit the banner at the top of generated files" + , core_enabled = True &= explicit &= name "core" &= help "Generate core serialization definitions (true by default, --core=false to disable)" + , comm_enabled = False &= explicit &= name "comm" &= help "Generate comm definitions" + , grpc_enabled = False &= explicit &= name "grpc" &= help "Generate gRPC definitions" + , service_inheritance_enabled = False &= explicit &= name "enable-service-inheritance" &= help "Enable service inheritance syntax in IDL" } &= name "c++" &= help "Generate C++ code"
bond.cabal view
@@ -2,7 +2,7 @@ -- Licensed under the MIT license. See LICENSE file in the project root for full license information. name: bond -version: 0.9.0.0 +version: 0.10.0.0 cabal-version: >= 1.8 tested-with: GHC>=7.4.1 synopsis: Bond schema compiler and code generator @@ -65,6 +65,7 @@ Language.Bond.Codegen.Cpp.Types_h Language.Bond.Codegen.Cpp.Comm_cpp Language.Bond.Codegen.Cpp.Comm_h + Language.Bond.Codegen.Cpp.Grpc_h Language.Bond.Codegen.Cs.Types_cs Language.Bond.Codegen.Cs.Comm_cs Language.Bond.Codegen.Cs.Grpc_cs @@ -78,8 +79,10 @@ test-suite gbc-tests type: exitcode-stdio-1.0 hs-source-dirs: tests, . - main-is: Main.hs - other-modules: Tests.Codegen + main-is: TestMain.hs + other-modules: IO + Options + Tests.Codegen Tests.Codegen.Util Tests.Syntax ghc-options: -threaded -Wall
src/Language/Bond/Codegen/Cpp/ApplyOverloads.hs view
@@ -22,20 +22,23 @@ -- Apply overloads applyOverloads :: [Protocol] -> MappingContext -> Text -> Text -> Declaration -> Text -applyOverloads protocols cpp attr body s@Struct {..} | null declParams = [lt| +applyOverloads protocols cpp attr extern s@Struct {..} | null declParams = [lt| // - // Overloads of Apply function with common transforms for #{declName}. - // These overloads will be selected using argument dependent lookup - // before ::bond::Apply function templates. + // Extern template specializations of Apply function with common + // transforms for #{declName}. // - #{attr}bool Apply(const ::bond::To< #{qualifiedName}>& transform, - const ::bond::bonded< #{qualifiedName}>& value)#{body} - #{attr}bool Apply(const ::bond::InitSchemaDef& transform, - const #{qualifiedName}& value)#{body} + #{extern}template #{attr} + bool Apply(const ::bond::To< #{qualifiedName}>& transform, + const ::bond::bonded< #{qualifiedName}>& value); - #{attr}bool Apply(const ::bond::Null& transform, - const ::bond::bonded< #{qualifiedName}, ::bond::SimpleBinaryReader< ::bond::InputBuffer>&>& value)#{body} + #{extern}template #{attr} + bool Apply(const ::bond::InitSchemaDef& transform, + const #{qualifiedName}& value); + + #{extern}template #{attr} + bool Apply(const ::bond::Null& transform, + const ::bond::bonded< #{qualifiedName}, ::bond::SimpleBinaryReader< ::bond::InputBuffer>&>& value); #{newlineSep 1 applyOverloads' protocols}|] where qualifiedName = getDeclTypeName cpp s @@ -49,24 +52,29 @@ deserialization (ProtocolWriter _) = mempty deserialization (ProtocolReader protocolReader) = [lt| - #{attr}bool Apply(const ::bond::To< #{qualifiedName}>& transform, - const ::bond::bonded< #{qualifiedName}, #{protocolReader}&>& value)#{body} + #{extern}template #{attr} + bool Apply(const ::bond::To< #{qualifiedName}>& transform, + const ::bond::bonded< #{qualifiedName}, #{protocolReader}&>& value); - #{attr}bool Apply(const ::bond::To< #{qualifiedName}>& transform, - const ::bond::bonded<void, #{protocolReader}&>& value)#{body}|] + #{extern}template #{attr} + bool Apply(const ::bond::To< #{qualifiedName}>& transform, + const ::bond::bonded<void, #{protocolReader}&>& value);|] serialization (ProtocolReader _) _ = mempty serialization (ProtocolWriter protocolWriter) transform = [lt| - #{attr}bool Apply(const ::bond::#{transform}<#{protocolWriter} >& transform, - const #{qualifiedName}& value)#{body} + #{extern}template #{attr} + bool Apply(const ::bond::#{transform}<#{protocolWriter} >& transform, + const #{qualifiedName}& value); - #{attr}bool Apply(const ::bond::#{transform}<#{protocolWriter} >& transform, - const ::bond::bonded< #{qualifiedName}>& value)#{body} + #{extern}template #{attr} + bool Apply(const ::bond::#{transform}<#{protocolWriter} >& transform, + const ::bond::bonded< #{qualifiedName}>& value); #{newlineSep 1 transcoding protocols}|] where transcoding (ProtocolWriter _) = mempty transcoding (ProtocolReader protocolReader) = [lt| - #{attr}bool Apply(const ::bond::#{transform}<#{protocolWriter} >& transform, - const ::bond::bonded< #{qualifiedName}, #{protocolReader}&>& value)#{body}|] + #{extern}template #{attr} + bool Apply(const ::bond::#{transform}<#{protocolWriter} >& transform, + const ::bond::bonded< #{qualifiedName}, #{protocolReader}&>& value);|] applyOverloads _ _ _ _ _ = mempty
src/Language/Bond/Codegen/Cpp/Apply_cpp.hs view
@@ -11,7 +11,6 @@ import Language.Bond.Codegen.TypeMapping import Language.Bond.Codegen.Util import Language.Bond.Codegen.Cpp.ApplyOverloads -import qualified Language.Bond.Codegen.Cpp.Util as CPP -- | Codegen template for generating /base_name/_apply.cpp containing -- definitions of the @Apply@ function overloads for the specified protocols. @@ -21,15 +20,12 @@ #include "#{file}_apply.h" #include "#{file}_reflection.h" -#{CPP.openNamespace cpp} - #{newlineSepEnd 1 (applyOverloads protocols cpp attr body) declarations} -#{CPP.closeNamespace cpp} +namespace bond +{ + #{newlineSepEnd 1 (applyOverloads protocols cpp attr extern) declarations} +} // namespace bond |]) where - body = [lt| - { - return ::bond::Apply<>(transform, value); - }|] - - attr = [lt||] + attr = "" + extern = ""
src/Language/Bond/Codegen/Cpp/Apply_h.hs view
@@ -14,7 +14,6 @@ import Language.Bond.Codegen.Util import Language.Bond.Codegen.TypeMapping import Language.Bond.Codegen.Cpp.ApplyOverloads -import qualified Language.Bond.Codegen.Cpp.Util as CPP -- | Codegen template for generating /base_name/_apply.h containing declarations of -- <https://microsoft.github.io/bond/manual/bond_cpp.html#optimizing-build-time Apply> @@ -30,15 +29,15 @@ #include <bond/stream/output_buffer.h> #{newlineSep 0 includeImport imports} -#{CPP.openNamespace cpp} - #{newlineSepEnd 1 (applyOverloads protocols cpp export_attr semi) declarations} -#{CPP.closeNamespace cpp} +namespace bond +{ + #{newlineSepEnd 1 (applyOverloads protocols cpp export_attr extern) declarations} +} // namespace bond |]) where includeImport (Import path) = [lt|#include "#{dropExtension path}_apply.h"|] - export_attr = optional (\a -> [lt|#{a} - |]) export_attribute + export_attr = optional (\a -> [lt|#{a}|]) export_attribute - semi = [lt|;|] + extern = "extern "
+ src/Language/Bond/Codegen/Cpp/Grpc_h.hs view
@@ -0,0 +1,291 @@+-- Copyright (c) Microsoft. All rights reserved. +-- Licensed under the MIT license. See LICENSE file in the project root for full license information. + +{-# LANGUAGE QuasiQuotes, OverloadedStrings, RecordWildCards #-} + +module Language.Bond.Codegen.Cpp.Grpc_h (grpc_h) where + +import System.FilePath +import Data.Monoid +import Prelude +import qualified Data.Text.Lazy as L +import Data.Text.Lazy.Builder +import Text.Shakespeare.Text +import Language.Bond.Syntax.Types +import Language.Bond.Codegen.Util +import Language.Bond.Codegen.TypeMapping +import qualified Language.Bond.Codegen.Cpp.Util as CPP + + +-- | Codegen template for generating /base_name/_grpc.h containing declarations of +-- of service interface and proxy. +grpc_h :: Maybe String -> MappingContext -> String -> [Import] -> [Declaration] -> (String, L.Text) +grpc_h _ cpp file imports declarations = ("_grpc.h", [lt| +#pragma once + +#include "#{file}_reflection.h" +#include "#{file}_types.h" +#{newlineSep 0 includeImport imports} +#include <bond/core/bonded.h> +#include <bond/ext/grpc/bond_utils.h> +#include <bond/ext/grpc/client_callback.h> +#include <bond/ext/grpc/io_manager.h> +#include <bond/ext/grpc/thread_pool.h> +#include <bond/ext/grpc/unary_call.h> +#include <bond/ext/grpc/detail/client_call_data.h> +#include <bond/ext/grpc/detail/service.h> +#include <bond/ext/grpc/detail/service_call_data.h> + +#include <boost/optional/optional.hpp> +#include <functional> +#include <memory> + +#ifdef _MSC_VER +#pragma warning (push) +#pragma warning (disable: 4100 4267) +#endif + +#include <grpc++/impl/codegen/channel_interface.h> +#include <grpc++/impl/codegen/client_context.h> +#include <grpc++/impl/codegen/completion_queue.h> +#include <grpc++/impl/codegen/rpc_method.h> +#include <grpc++/impl/codegen/status.h> + +#ifdef _MSC_VER +#pragma warning (pop) +#endif + +#{CPP.openNamespace cpp} +#{doubleLineSep 1 grpc declarations} + +#{CPP.closeNamespace cpp} + +|]) + where + includeImport (Import path) = [lt|#include "#{dropExtension path}_grpc.h"|] + + idl = MappingContext idlTypeMapping [] [] [] + + cppType = getTypeName cpp + + payload = maybe "::bond::Void" cppType + + bonded mt = bonded' (payload mt) + where + bonded' params = [lt|::bond::bonded<#{padLeft}#{params}>|] + where + paramsText = toLazyText params + padLeft = if L.head paramsText == ':' then [lt| |] else mempty + + grpc s@Service{..} = [lt| +#{template}class #{declName} final +{ +public: + template <typename TThreadPool> + class #{proxyName} + { + public: + #{proxyName}( + const std::shared_ptr< ::grpc::ChannelInterface>& channel, + std::shared_ptr< ::bond::ext::gRPC::io_manager> ioManager, + std::shared_ptr<TThreadPool> threadPool); + + #{doubleLineSep 2 publicProxyMethodDecl serviceMethods} + + #{proxyName}(const #{proxyName}&) = delete; + #{proxyName}& operator=(const #{proxyName}&) = delete; + + #{proxyName}(#{proxyName}&&) = default; + #{proxyName}& operator=(#{proxyName}&&) = default; + + private: + std::shared_ptr< ::grpc::ChannelInterface> _channel; + std::shared_ptr< ::bond::ext::gRPC::io_manager> _ioManager; + std::shared_ptr<TThreadPool> _threadPool; + + #{doubleLineSep 2 privateProxyMethodDecl serviceMethods} + }; + + using Client = #{proxyName}< ::bond::ext::gRPC::thread_pool>; + + template <typename TThreadPool> + class #{serviceName} : public ::bond::ext::gRPC::detail::service<TThreadPool> + { + public: + #{serviceName}() + { + #{newlineSep 3 serviceAddMethod serviceMethods} + } + + virtual ~#{serviceName}() { } + #{serviceStartMethod} + + #{newlineSep 2 serviceVirtualMethod serviceMethods} + + private: + #{newlineSep 2 serviceMethodReceiveData serviceMethods} + }; + + using Service = #{serviceName}< ::bond::ext::gRPC::thread_pool>; +}; + +#{template}template <typename TThreadPool> +inline #{className}::#{proxyName}<TThreadPool>::#{proxyName}( + const std::shared_ptr< ::grpc::ChannelInterface>& channel, + std::shared_ptr< ::bond::ext::gRPC::io_manager> ioManager, + std::shared_ptr<TThreadPool> threadPool) + : _channel(channel) + , _ioManager(ioManager) + , _threadPool(threadPool) + #{newlineSep 1 proxyMethodMemberInit serviceMethods} + { } + +#{doubleLineSep 0 methodDecl serviceMethods} +|] + where + className = CPP.className s + template = CPP.template s + proxyName = "ClientCore" :: String + serviceName = "ServiceCore" :: String + + methodNames :: [String] + methodNames = map methodName serviceMethods + + serviceMethodsWithIndex :: [(Integer,Method)] + serviceMethodsWithIndex = zip [0..] serviceMethods + + publicProxyMethodDecl Function{methodInput = Nothing, ..} = [lt|void Async#{methodName}(::std::shared_ptr< ::grpc::ClientContext> context, const std::function<void(std::shared_ptr< ::bond::ext::gRPC::unary_call_result< #{payload methodResult}>>)>& cb); + void Async#{methodName}(const std::function<void(std::shared_ptr< ::bond::ext::gRPC::unary_call_result< #{payload methodResult}>>)>& cb) + { + Async#{methodName}(::std::make_shared< ::grpc::ClientContext>(), cb); + }|] + publicProxyMethodDecl Function{..} = [lt|void Async#{methodName}(::std::shared_ptr< ::grpc::ClientContext> context, const #{bonded methodInput}& request, const std::function<void(std::shared_ptr< ::bond::ext::gRPC::unary_call_result< #{payload methodResult}>>)>& cb); + void Async#{methodName}(::std::shared_ptr< ::grpc::ClientContext> context, const #{payload methodInput}& request, const std::function<void(std::shared_ptr< ::bond::ext::gRPC::unary_call_result< #{payload methodResult}>>)>& cb) + { + Async#{methodName}(context, #{bonded methodInput}{request}, cb); + } + void Async#{methodName}(const #{bonded methodInput}& request, const std::function<void(std::shared_ptr< ::bond::ext::gRPC::unary_call_result< #{payload methodResult}>>)>& cb) + { + Async#{methodName}(::std::make_shared< ::grpc::ClientContext>(), request, cb); + } + void Async#{methodName}(const #{payload methodInput}& request, const std::function<void(std::shared_ptr< ::bond::ext::gRPC::unary_call_result< #{payload methodResult}>>)>& cb) + { + Async#{methodName}(::std::make_shared< ::grpc::ClientContext>(), #{bonded methodInput}{request}, cb); + }|] + publicProxyMethodDecl Event{methodInput = Nothing, ..} = [lt|void Async#{methodName}(::std::shared_ptr< ::grpc::ClientContext> context); + void Async#{methodName}() + { + Async#{methodName}(::std::make_shared< ::grpc::ClientContext>()); + }|] + publicProxyMethodDecl Event{..} = [lt|void Async#{methodName}(::std::shared_ptr< ::grpc::ClientContext> context, const #{bonded methodInput}& request); + void Async#{methodName}(::std::shared_ptr< ::grpc::ClientContext> context, const #{payload methodInput}& request) + { + Async#{methodName}(context, #{bonded methodInput}{request}); + } + void Async#{methodName}(const #{bonded methodInput}& request) + { + Async#{methodName}(::std::make_shared< ::grpc::ClientContext>(), request); + } + void Async#{methodName}(const #{payload methodInput}& request) + { + Async#{methodName}(::std::make_shared< ::grpc::ClientContext>(), #{bonded methodInput}{request}); + }|] + + privateProxyMethodDecl Function{..} = [lt|const ::grpc::RpcMethod rpcmethod_#{methodName}_;|] + privateProxyMethodDecl Event{..} = [lt|const ::grpc::RpcMethod rpcmethod_#{methodName}_;|] + + proxyMethodMemberInit Function{..} = [lt|, rpcmethod_#{methodName}_("/#{getDeclTypeName idl s}/#{methodName}", ::grpc::RpcMethod::NORMAL_RPC, channel)|] + proxyMethodMemberInit Event{..} = [lt|, rpcmethod_#{methodName}_("/#{getDeclTypeName idl s}/#{methodName}", ::grpc::RpcMethod::NORMAL_RPC, channel)|] + + methodDecl Function{..} = [lt|#{template}template <typename TThreadPool> +inline void #{className}::#{proxyName}<TThreadPool>::Async#{methodName}( + ::std::shared_ptr< ::grpc::ClientContext> context, + #{voidParam methodInput} + const std::function<void(std::shared_ptr< ::bond::ext::gRPC::unary_call_result< #{payload methodResult}>>)>& cb) +{ + #{voidRequest methodInput} + auto calldata = std::make_shared< ::bond::ext::gRPC::detail::client_unary_call_data< #{payload methodInput}, #{payload methodResult}, TThreadPool>>( + _channel, + _ioManager, + _threadPool, + context, + cb); + calldata->dispatch(rpcmethod_#{methodName}_, request); +}|] + where + voidRequest Nothing = [lt|auto request = ::bond::bonded< ::bond::Void>{ ::bond::Void()};|] + voidRequest _ = mempty + voidParam Nothing = mempty + voidParam _ = [lt|const #{bonded methodInput}& request,|] + + methodDecl Event{..} = [lt|#{template}template <typename TThreadPool> +inline void #{className}::#{proxyName}<TThreadPool>::Async#{methodName}( + ::std::shared_ptr< ::grpc::ClientContext> context + #{voidParam methodInput}) +{ + #{voidRequest methodInput} + auto calldata = std::make_shared< ::bond::ext::gRPC::detail::client_unary_call_data< #{payload methodInput}, #{payload Nothing}, TThreadPool>>( + _channel, + _ioManager, + _threadPool, + context); + calldata->dispatch(rpcmethod_#{methodName}_, request); +}|] + where + voidRequest Nothing = [lt|auto request = ::bond::bonded< ::bond::Void>{ ::bond::Void()};|] + voidRequest _ = mempty + voidParam Nothing = mempty + voidParam _ = [lt|, const #{bonded methodInput}& request|] + + serviceAddMethod Function{..} = [lt|this->AddMethod("/#{getDeclTypeName idl s}/#{methodName}");|] + serviceAddMethod Event{..} = [lt|this->AddMethod("/#{getDeclTypeName idl s}/#{methodName}");|] + + serviceStartMethod = [lt|virtual void start( + ::grpc::ServerCompletionQueue* #{cqParam}, + std::shared_ptr<TThreadPool> #{tpParam}) override + { + BOOST_ASSERT(#{cqParam}); + BOOST_ASSERT(#{tpParam}); + + #{newlineSep 3 initMethodReceiveData serviceMethodsWithIndex} + + #{newlineSep 3 queueReceive serviceMethodsWithIndex} + }|] + where cqParam = uniqueName "cq" methodNames + tpParam = uniqueName "tp" methodNames + initMethodReceiveData (index,Function{..}) = [lt|#{serviceRdMember methodName}.emplace( + this, + #{index}, + #{cqParam}, + #{tpParam}, + std::bind(&#{serviceName}::#{methodName}, this, std::placeholders::_1));|] + initMethodReceiveData (index,Event{..}) = [lt|#{serviceRdMember methodName}.emplace( + this, + #{index}, + #{cqParam}, + #{tpParam}, + std::bind(&#{serviceName}::#{methodName}, this, std::placeholders::_1));|] + queueReceive (index,Function{..}) = [lt|this->queue_receive( + #{index}, + &#{serviceRdMember methodName}->_receivedCall->_context, + &#{serviceRdMember methodName}->_receivedCall->_request, + &#{serviceRdMember methodName}->_receivedCall->_responder, + #{cqParam}, + &#{serviceRdMember methodName}.get());|] + queueReceive (index,Event{..}) = [lt|this->queue_receive( + #{index}, + &#{serviceRdMember methodName}->_receivedCall->_context, + &#{serviceRdMember methodName}->_receivedCall->_request, + &#{serviceRdMember methodName}->_receivedCall->_responder, + #{cqParam}, + &#{serviceRdMember methodName}.get());|] + + serviceMethodReceiveData Function{..} = [lt|::boost::optional< ::bond::ext::gRPC::detail::service_unary_call_data< #{bonded methodInput}, #{payload methodResult}, TThreadPool>> #{serviceRdMember methodName};|] + serviceMethodReceiveData Event{..} = [lt|::boost::optional< ::bond::ext::gRPC::detail::service_unary_call_data< #{bonded methodInput}, #{payload Nothing}, TThreadPool>> #{serviceRdMember methodName};|] + + serviceVirtualMethod Function{..} = [lt|virtual void #{methodName}(::bond::ext::gRPC::unary_call< #{bonded methodInput}, #{payload methodResult}>) = 0;|] + serviceVirtualMethod Event{..} = [lt|virtual void #{methodName}(::bond::ext::gRPC::unary_call< #{bonded methodInput}, #{payload Nothing}>) = 0;|] + + serviceRdMember methodName = uniqueName ("_rd_" ++ methodName) methodNames + + grpc _ = mempty
src/Language/Bond/Codegen/Cpp/Reflection_h.hs view
@@ -107,14 +107,14 @@ fieldTemplates = F.foldMap $ \ f@Field {..} -> [lt| // #{fieldName} - typedef ::bond::reflection::FieldTemplate< + typedef struct : ::bond::reflection::FieldTemplate< #{fieldOrdinal}, #{CPP.modifierTag f}, #{className}, #{cppType fieldType}, &#{className}::#{fieldName}, &s_#{fieldName}_metadata - > #{fieldName}; + > {} #{fieldName}; |]
src/Language/Bond/Codegen/Templates.hs view
@@ -44,6 +44,7 @@ -- ** C++ Comm , comm_h , comm_cpp + , grpc_h -- ** C# , FieldMapping(..) , StructMapping(..) @@ -64,6 +65,7 @@ import Language.Bond.Codegen.Cpp.Types_h import Language.Bond.Codegen.Cpp.Comm_cpp import Language.Bond.Codegen.Cpp.Comm_h +import Language.Bond.Codegen.Cpp.Grpc_h import Language.Bond.Codegen.Cs.Types_cs import Language.Bond.Codegen.Cs.Comm_cs import Language.Bond.Codegen.Cs.Grpc_cs
src/Language/Bond/Parser.hs view
@@ -327,6 +327,15 @@ Right (Service {..}, _) -> fail $ "Unexpected service " ++ declName ++ ". Expected struct, enum or alias." Right (decl, args) -> return $ BT_UserDefined decl args +-- parser for service type +serviceType :: Parser Type +serviceType = do + symbol_ <- userSymbol + case symbol_ of + Right (decl@Service{}, args) -> return $ BT_UserDefined decl args + Right (decl, _) -> fail $ "Unexpected type " ++ (declName decl) ++ ". Expected a service." + Left param -> fail $ "Unexpected type parameter " ++ (paramName param) ++ ". Expected a service." + userSymbol :: Parser (Either TypeParam (Declaration, [Type])) userSymbol = do name <- qualifiedName @@ -369,8 +378,9 @@ name <- keyword "service" *> identifier <?> "service definition" params <- parameters namespaces <- asks currentNamespaces - local (with params) $ Service namespaces attr name params <$> methods <* optional semi + local (with params) $ Service namespaces attr name params <$> base <*> methods <* optional semi where + base = optional (colon *> serviceType <?> "base service") with params e = e { currentParams = params } methods = unique $ braces $ semiEnd (try event <|> try function) unique p = do
src/Language/Bond/Syntax/Types.hs view
@@ -173,6 +173,7 @@ , declAttributes :: [Attribute] -- zero or more attributes , declName :: String -- service name , declParams :: [TypeParam] -- type parameters for generic service + , serviceBase :: Maybe Type -- optional base service , serviceMethods :: [Method] -- zero or more methods } -- ^ <https://microsoft.github.io/bond/manual/compiler.html#service-definition service definition> deriving (Eq, Show)
− tests/Main.hs
@@ -1,163 +0,0 @@--- Copyright (c) Microsoft. All rights reserved. --- Licensed under the MIT license. See LICENSE file in the project root for full license information. - -import Test.Tasty -import Test.Tasty.QuickCheck -import Test.Tasty.HUnit (testCase) -import Tests.Syntax -import Tests.Codegen -import Tests.Codegen.Util(utilTestGroup) - -tests :: TestTree -tests = testGroup "Compiler tests" - [ testGroup "AST" - [ localOption (QuickCheckMaxSize 6) $ - testProperty "roundtrip" roundtripAST - , testGroup "Compare .bond and .json" - [ testCase "attributes" $ compareAST "attributes" - , testCase "basic types" $ compareAST "basic_types" - , testCase "bond_meta types" $ compareAST "bond_meta" - , testCase "complex types" $ compareAST "complex_types" - , testCase "default values" $ compareAST "defaults" - , testCase "empty" $ compareAST "empty" - , testCase "field modifiers" $ compareAST "field_modifiers" - , testCase "generics" $ compareAST "generics" - , testCase "inheritance" $ compareAST "inheritance" - , testCase "type aliases" $ compareAST "aliases" - , testCase "documentation example" $ compareAST "example" - , testCase "simple service syntax" $ compareAST "service" - , testCase "service attributes" $ compareAST "service_attributes" - , testCase "generic service" $ compareAST "generic_service" - , testCase "documentation example" $ compareAST "example" - ] - ] - , testGroup "SchemaDef" - [ verifySchemaDef "attributes" "Foo" - , verifySchemaDef "basic_types" "BasicTypes" - , verifySchemaDef "defaults" "Foo" - , verifySchemaDef "field_modifiers" "Foo" - , verifySchemaDef "inheritance" "Foo" - , verifySchemaDef "alias_key" "foo" - , verifySchemaDef "maybe_blob" "Foo" - , verifySchemaDef "nullable_alias" "foo" - , verifySchemaDef "schemadef" "AliasBase" - , verifySchemaDef "schemadef" "EnumDefault" - , verifySchemaDef "schemadef" "StringTree" - , verifySchemaDef "example" "SomeStruct" - ] - , testGroup "Types" - [ testCase "type alias resolution" aliasResolution - ] - , testGroup "Codegen Failures (Expect to see errors below check for OK or FAIL)" - [ testCase "Struct default value nothing" $ failBadSyntax "Should fail when default value of a struct field is 'nothing'" "struct_nothing" - , testCase "Enum no default value" $ failBadSyntax "Should fail when an enum field has no default value" "enum_no_default" - , testCase "Alias default value" $ failBadSyntax "Should fail when underlying default value is of the wrong type" "aliases_default" - , testCase "Out of range" $ failBadSyntax "Should fail, out of range for int16" "int_out_of_range" - , testCase "Duplicate method definition in service" $ failBadSyntax "Should fail, method name should be unique" "duplicate_service_method" - ] - , testGroup "Codegen" - [ utilTestGroup, - testGroup "C++" - [ verifyCppCodegen "attributes" - , verifyCppCodegen "basic_types" - , verifyCppCodegen "bond_meta" - , verifyCppCodegen "complex_types" - , verifyCppCodegen "defaults" - , verifyCppCodegen "empty" - , verifyCppCodegen "field_modifiers" - , verifyCppCodegen "generics" - , verifyCppCodegen "inheritance" - , verifyCppCodegen "aliases" - , verifyCppCodegen "alias_key" - , verifyCppCodegen "maybe_blob" - , verifyCodegen - [ "c++" - , "--enum-header" - ] - "with_enum_header" - , verifyCodegen - [ "c++" - , "--allocator=arena" - ] - "alias_with_allocator" - , verifyCodegen - [ "c++" - , "--allocator=arena" - , "--using=List=my::list<{0}, arena>" - , "--using=Vector=my::vector<{0}, arena>" - , "--using=Set=my::set<{0}, arena>" - , "--using=Map=my::map<{0}, {1}, arena>" - , "--using=String=my::string<arena>" - ] - "custom_alias_with_allocator" - , verifyCodegen - [ "c++" - , "--allocator=arena" - , "--using=List=my::list<{0}>" - , "--using=Vector=my::vector<{0}>" - , "--using=Set=my::set<{0}>" - , "--using=Map=my::map<{0}, {1}>" - , "--using=String=my::string" - ] - "custom_alias_without_allocator" - , testGroup "Apply" - [ verifyApplyCodegen - [ "c++" - , "--apply-attribute=DllExport" - ] - "basic_types" - ] - , testGroup "Exports" - [ verifyExportsCodegen - [ "c++" - , "--export-attribute=DllExport" - ] - "service" - ] - , testGroup "Comm" - [ verifyCppCommCodegen - [ "c++" - ] - "service" - , verifyCppCommCodegen - [ "c++" - ] - "generic_service" - , verifyCppCommCodegen - [ "c++" - ] - "service_attributes" - ] - ] - , testGroup "C#" - [ verifyCsCodegen "attributes" - , verifyCsCodegen "basic_types" - , verifyCsCodegen "bond_meta" - , verifyCsCodegen "complex_types" - , verifyCsCodegen "defaults" - , verifyCsCodegen "empty" - , verifyCsCodegen "field_modifiers" - , verifyCsCodegen "generics" - , verifyCsCodegen "inheritance" - , verifyCsCodegen "aliases" - , verifyCodegen - [ "c#" - , "--using=time=System.DateTime" - ] - "nullable_alias" - , testGroup "Comm" - [ verifyCsCommCodegen - [ "c#" - ] - "service" - , verifyCsCommCodegen - [ "c#" - ] - "generic_service" - ] - ] - ] - ] - -main :: IO () -main = defaultMain tests
+ tests/TestMain.hs view
@@ -0,0 +1,180 @@+-- Copyright (c) Microsoft. All rights reserved. +-- Licensed under the MIT license. See LICENSE file in the project root for full license information. + +import Test.Tasty +import Test.Tasty.QuickCheck +import Test.Tasty.HUnit (testCase) +import Tests.Syntax +import Tests.Codegen +import Tests.Codegen.Util(utilTestGroup) + +tests :: TestTree +tests = testGroup "Compiler tests" + [ testGroup "AST" + [ localOption (QuickCheckMaxSize 6) $ + testProperty "roundtrip" roundtripAST + , testGroup "Compare .bond and .json" + [ testCase "attributes" $ compareAST "attributes" + , testCase "basic types" $ compareAST "basic_types" + , testCase "bond_meta types" $ compareAST "bond_meta" + , testCase "complex types" $ compareAST "complex_types" + , testCase "default values" $ compareAST "defaults" + , testCase "empty" $ compareAST "empty" + , testCase "field modifiers" $ compareAST "field_modifiers" + , testCase "generics" $ compareAST "generics" + , testCase "inheritance" $ compareAST "inheritance" + , testCase "type aliases" $ compareAST "aliases" + , testCase "documentation example" $ compareAST "example" + , testCase "simple service syntax" $ compareAST "service" + , testCase "service attributes" $ compareAST "service_attributes" + , testCase "generic service" $ compareAST "generic_service" + , testCase "documentation example" $ compareAST "example" + , testCase "service inheritance" $ compareAST "service_inheritance" + ] + ] + , testGroup "SchemaDef" + [ verifySchemaDef "attributes" "Foo" + , verifySchemaDef "basic_types" "BasicTypes" + , verifySchemaDef "defaults" "Foo" + , verifySchemaDef "field_modifiers" "Foo" + , verifySchemaDef "inheritance" "Foo" + , verifySchemaDef "alias_key" "foo" + , verifySchemaDef "maybe_blob" "Foo" + , verifySchemaDef "nullable_alias" "foo" + , verifySchemaDef "schemadef" "AliasBase" + , verifySchemaDef "schemadef" "EnumDefault" + , verifySchemaDef "schemadef" "StringTree" + , verifySchemaDef "example" "SomeStruct" + ] + , testGroup "Types" + [ testCase "type alias resolution" aliasResolution + ] + , testGroup "Codegen Failures (Expect to see errors below check for OK or FAIL)" + [ testCase "Struct default value nothing" $ failBadSyntax "Should fail when default value of a struct field is 'nothing'" "struct_nothing" + , testCase "Enum no default value" $ failBadSyntax "Should fail when an enum field has no default value" "enum_no_default" + , testCase "Alias default value" $ failBadSyntax "Should fail when underlying default value is of the wrong type" "aliases_default" + , testCase "Out of range" $ failBadSyntax "Should fail, out of range for int16" "int_out_of_range" + , testCase "Duplicate method definition in service" $ failBadSyntax "Should fail, method name should be unique" "duplicate_service_method" + , testCase "Invalid service base: struct" $ failBadSyntax "Should fail, struct can't be used as service base" "service_invalid_base_struct" + , testCase "Invalid service base: type param" $ failBadSyntax "Should fail, type param can't be used as service base" "service_invalid_base_type_param" + ] + , testGroup "Codegen" + [ utilTestGroup, + testGroup "C++" + [ verifyCppCodegen "attributes" + , verifyCppCodegen "basic_types" + , verifyCppCodegen "bond_meta" + , verifyCppCodegen "complex_types" + , verifyCppCodegen "defaults" + , verifyCppCodegen "empty" + , verifyCppCodegen "field_modifiers" + , verifyCppCodegen "generics" + , verifyCppCodegen "inheritance" + , verifyCppCodegen "aliases" + , verifyCppCodegen "alias_key" + , verifyCppCodegen "maybe_blob" + , verifyCodegen + [ "c++" + , "--enum-header" + ] + "with_enum_header" + , verifyCodegen + [ "c++" + , "--allocator=arena" + ] + "alias_with_allocator" + , verifyCodegen + [ "c++" + , "--allocator=arena" + , "--using=List=my::list<{0}, arena>" + , "--using=Vector=my::vector<{0}, arena>" + , "--using=Set=my::set<{0}, arena>" + , "--using=Map=my::map<{0}, {1}, arena>" + , "--using=String=my::string<arena>" + ] + "custom_alias_with_allocator" + , verifyCodegen + [ "c++" + , "--allocator=arena" + , "--using=List=my::list<{0}>" + , "--using=Vector=my::vector<{0}>" + , "--using=Set=my::set<{0}>" + , "--using=Map=my::map<{0}, {1}>" + , "--using=String=my::string" + ] + "custom_alias_without_allocator" + , testGroup "Apply" + [ verifyApplyCodegen + [ "c++" + , "--apply-attribute=DllExport" + ] + "basic_types" + ] + , testGroup "Exports" + [ verifyExportsCodegen + [ "c++" + , "--export-attribute=DllExport" + ] + "service" + ] + , testGroup "Comm" + [ verifyCppCommCodegen + [ "c++" + ] + "service" + , verifyCppCommCodegen + [ "c++" + ] + "generic_service" + , verifyCppCommCodegen + [ "c++" + ] + "service_attributes" + ] + , testGroup "Grpc" + [ verifyCppGrpcCodegen + [ "c++" + ] + "service" + , verifyCppGrpcCodegen + [ "c++" + ] + "generic_service" + , verifyCppGrpcCodegen + [ "c++" + ] + "service_attributes" + ] + ] + , testGroup "C#" + [ verifyCsCodegen "attributes" + , verifyCsCodegen "basic_types" + , verifyCsCodegen "bond_meta" + , verifyCsCodegen "complex_types" + , verifyCsCodegen "defaults" + , verifyCsCodegen "empty" + , verifyCsCodegen "field_modifiers" + , verifyCsCodegen "generics" + , verifyCsCodegen "inheritance" + , verifyCsCodegen "aliases" + , verifyCodegen + [ "c#" + , "--using=time=System.DateTime" + ] + "nullable_alias" + , testGroup "Comm" + [ verifyCsCommCodegen + [ "c#" + ] + "service" + , verifyCsCommCodegen + [ "c#" + ] + "generic_service" + ] + ] + ] + ] + +main :: IO () +main = defaultMain tests
tests/Tests/Codegen.hs view
@@ -8,6 +8,7 @@ ( verifyCodegen , verifyCppCodegen , verifyCppCommCodegen + , verifyCppGrpcCodegen , verifyApplyCodegen , verifyExportsCodegen , verifyCsCodegen @@ -84,6 +85,17 @@ templates = [ comm_h (export_attribute options) , comm_cpp + , types_cpp + ] + +verifyCppGrpcCodegen :: [String] -> FilePath -> TestTree +verifyCppGrpcCodegen args baseName = + testGroup baseName $ + map (verifyFile options baseName cppTypeMapping "") templates + where + options = processOptions args + templates = + [ grpc_h (export_attribute options) , types_cpp ]