packages feed

bond 0.10.0.0 → 0.10.1.0

raw patch · 13 files changed

+166/−32 lines, 13 filesnew-uploaderPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Language.Bond.Codegen.Templates: grpc_cpp :: MappingContext -> String -> [Import] -> [Declaration] -> (String, Text)

Files

Main.hs view
@@ -87,7 +87,7 @@     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])
+                        , (grpc_enabled, [grpc_h export_attribute, grpc_cpp])
                         ]
     core_files = [
           reflection_h export_attribute
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.10.0.0
+version:            0.10.1.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_cpp
                     Language.Bond.Codegen.Cpp.Grpc_h
                     Language.Bond.Codegen.Cs.Types_cs
                     Language.Bond.Codegen.Cs.Comm_cs
@@ -86,6 +87,12 @@                     Tests.Codegen.Util
                     Tests.Syntax
   ghc-options:      -threaded -Wall
+
+  if os(windows) && arch(i386)
+    ld-options:     -Wl,--dynamicbase -Wl,--nxcompat -Wl,--large-address-aware
+  if os(windows) && arch(x86_64)
+    ld-options:     -Wl,--dynamicbase -Wl,--nxcompat -Wl,--high-entropy-va
+
   build-depends:    bond,
                     aeson >= 0.7.0.6 && < 0.12.0.0,
                     aeson-pretty == 0.7.2,
@@ -112,6 +119,12 @@   other-modules:    IO
                     Options
   ghc-options:      -threaded -Wall
+
+  if os(windows) && arch(i386)
+    ld-options:     -Wl,--dynamicbase -Wl,--nxcompat -Wl,--large-address-aware
+  if os(windows) && arch(x86_64)
+    ld-options:     -Wl,--dynamicbase -Wl,--nxcompat -Wl,--high-entropy-va
+
   build-depends:    bond,
                     aeson >= 0.7.0.6 && < 0.12.0.0,
                     async >= 2.0.1.0,
src/Language/Bond/Codegen/Cpp/Comm_cpp.hs view
@@ -20,7 +20,6 @@ comm_cpp cpp file _imports declarations = ("_comm.cpp", [lt|
 #include "#{file}_reflection.h"
 #include "#{file}_comm.h"
-#include <bond/core/exception.h>
 
 #{CPP.openNamespace cpp}
     #{doubleLineSepEnd 1 statics declarations}
+ src/Language/Bond/Codegen/Cpp/Grpc_cpp.hs view
@@ -0,0 +1,33 @@+-- 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_cpp (grpc_cpp) where
+
+import Data.Monoid
+import Prelude
+import Data.Text.Lazy (Text)
+import Text.Shakespeare.Text
+import Language.Bond.Syntax.Types
+import Language.Bond.Codegen.TypeMapping
+import Language.Bond.Codegen.Util
+import qualified Language.Bond.Codegen.Cpp.Util as CPP
+
+-- | Codegen template for generating /base_name/_grpc.cpp containing
+-- definitions of helper functions and schema metadata static variables.
+grpc_cpp :: MappingContext -> String -> [Import] -> [Declaration] -> (String, Text)
+grpc_cpp cpp file _imports declarations = ("_grpc.cpp", [lt|
+#include "#{file}_reflection.h"
+#include "#{file}_grpc.h"
+
+#{CPP.openNamespace cpp}
+    #{doubleLineSepEnd 1 statics declarations}
+#{CPP.closeNamespace cpp}
+|])
+  where
+    -- definitions of Schema statics for non-generic services
+    statics s@Service {..} =
+        if null declParams then CPP.schemaMetadata cpp s else mempty
+
+    statics _ = mempty
src/Language/Bond/Codegen/Cpp/Grpc_h.hs view
@@ -11,6 +11,7 @@ import qualified Data.Text.Lazy as L
 import Data.Text.Lazy.Builder
 import Text.Shakespeare.Text
+import Language.Bond.Util
 import Language.Bond.Syntax.Types
 import Language.Bond.Codegen.Util
 import Language.Bond.Codegen.TypeMapping
@@ -20,7 +21,7 @@ -- | 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|
+grpc_h export_attribute cpp file imports declarations = ("_grpc.h", [lt|
 #pragma once
 
 #include "#{file}_reflection.h"
@@ -30,6 +31,7 @@ #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/reflection.h>
 #include <bond/ext/grpc/thread_pool.h>
 #include <bond/ext/grpc/unary_call.h>
 #include <bond/ext/grpc/detail/client_call_data.h>
@@ -81,6 +83,8 @@ #{template}class #{declName} final
 {
 public:
+    struct Schema;
+
     template <typename TThreadPool>
     class #{proxyName}
     {
@@ -141,10 +145,68 @@     { }
 
 #{doubleLineSep 0 methodDecl serviceMethods}
+
+#{template}struct #{className}::Schema
+{
+    #{export_attr}static const ::bond::Metadata metadata;
+
+    #{newlineSep 1 methodMetadata serviceMethods}
+
+    public: struct service
+    {
+        #{doubleLineSep 2 methodTemplate serviceMethods}
+    };
+
+    private: typedef boost::mpl::list<> methods0;
+    #{newlineSep 1 pushMethod indexedMethods}
+
+    public: typedef #{typename}methods#{length serviceMethods}::type methods;
+
+    #{constructor}
+};
+#{onlyTemplate $ CPP.schemaMetadata cpp s}
 |]
       where
         className = CPP.className s
         template = CPP.template s
+        onlyTemplate x = if null declParams then mempty else x
+        typename = onlyTemplate [lt|typename |]
+
+        export_attr = optional (\a -> [lt|#{a}
+        |]) export_attribute
+
+        methodMetadataVar m = [lt|s_#{methodName m}_metadata|]
+
+        methodMetadata m =
+            [lt|private: #{export_attr}static const ::bond::Metadata #{methodMetadataVar m};|]
+
+        -- reversed list of method names zipped with indexes
+        indexedMethods :: [(String, Int)]
+        indexedMethods = zipWith ((,) . methodName) (reverse serviceMethods) [0..]
+
+        pushMethod (method, i) =
+            [lt|private: typedef #{typename}boost::mpl::push_front<methods#{i}, #{typename}service::#{method}>::type methods#{i + 1};|]
+
+        -- constructor, generated only for service templates
+        constructor = onlyTemplate [lt|Schema()
+        {
+            // Force instantiation of template statics
+            (void)metadata;
+            #{newlineSep 3 static serviceMethods}
+        }|]
+          where
+            static m = [lt|(void)#{methodMetadataVar m};|]
+
+        methodTemplate m = [lt|typedef ::bond::ext::gRPC::reflection::MethodTemplate<
+                #{className},
+                #{bonded $ methodInput m},
+                #{result m},
+                &#{methodMetadataVar m}
+            > #{methodName m};|]
+          where
+            result Event{} = "void"
+            result Function{..} = bonded methodResult
+
         proxyName = "ClientCore" :: String
         serviceName = "ServiceCore" :: String
 
@@ -267,16 +329,16 @@                 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,
+                &#{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,
+                &#{serviceRdMember methodName}->_receivedCall->context(),
+                &#{serviceRdMember methodName}->_receivedCall->request(),
+                &#{serviceRdMember methodName}->_receivedCall->responder(),
                 #{cqParam},
                 &#{serviceRdMember methodName}.get());|]
 
src/Language/Bond/Codegen/Cpp/Types_h.hs view
@@ -35,7 +35,7 @@ #{newlineBeginSep 0 includeHeader userHeaders}
 #include <bond/core/bond_version.h>
 
-#if BOND_VERSION < 0x0520
+#if BOND_VERSION < 0x0700
 #error This file was generated by a newer version of the Bond compiler and is incompatible with your version of the Bond library.
 #endif
 
@@ -206,10 +206,23 @@         {
         }|]
 
+        needAlloc alloc = isJust structBase || any (allocParameterized alloc . fieldType) structFields
+        allocParameterized alloc t = (isStruct t) || (L.isInfixOf (L.pack alloc) . toLazyText $ cppType t)
+
         -- default constructor
         defaultCtor = [lt|
-        #{declName}()#{initList}#{ctorBody}|]
+        #{dummyTemplateTag}#{declName}(#{vc12WorkaroundParam})#{initList}#{ctorBody}|]
           where
+            needAllocParam = maybe False needAlloc allocator
+
+            vc12WorkaroundParam = if needAllocParam then [lt|_bond_vc12_ctor_workaround_ = {}|] else mempty
+
+            dummyTemplateTag = if needAllocParam
+                then [lt|struct _bond_vc12_ctor_workaround_ {};
+        template <int = 0> // Workaround to avoid compilation if not used
+        |]
+                else mempty
+
             initList = initializeList mempty
                 $ commaLineSep 3 fieldInit structFields
             fieldInit Field {..} = optional (\x -> [lt|#{fieldName}(#{x})|])
@@ -220,9 +233,7 @@         #{declName}(const #{alloc}&#{allocParam})#{initList}#{ctorBody}
         |]
           where
-            allocParam = if needAlloc then [lt| allocator|] else mempty
-              where
-                needAlloc = isJust structBase || any (allocParameterized . fieldType) structFields
+            allocParam = if needAlloc alloc then [lt| allocator|] else mempty
             initList = initializeList
                 (optional baseInit structBase)
                 (commaLineSep 3 fieldInit structFields)
@@ -230,7 +241,7 @@             fieldInit Field {..} = optional (\x -> [lt|#{fieldName}(#{x})|])
                 $ allocInitValue fieldType fieldDefault
             allocInitValue t@(BT_UserDefined a@Alias {} args) d
-                | allocParameterized t = allocInitValue (resolveAlias a args) d
+                | allocParameterized alloc t = allocInitValue (resolveAlias a args) d
                 | otherwise = initValue t d
             allocInitValue (BT_Nullable t) _ = allocInitValue t Nothing
             allocInitValue (BT_Maybe t) _ = allocInitValue t Nothing
@@ -244,7 +255,6 @@             keyType (BT_Map key _) = cppType key
             keyType (BT_UserDefined a@Alias {} args) = keyType $ resolveAlias a args
             keyType _ = error "allocatorCtor/keyType: impossible happened."
-            allocParameterized t = (isStruct t) || (L.isInfixOf (L.pack alloc) . toLazyText $ cppType t)
 
         -- copy constructor
         copyCtor = if hasMetaFields then define else implicitlyDeclared
src/Language/Bond/Codegen/Cs/Comm_cs.hs view
@@ -43,6 +43,8 @@ #{CS.disableReSharperWarnings}
 namespace #{csNamespace}
 {
+    using System.Collections.Generic;
+
     #{doubleLineSep 1 comm declarations}
 } // #{csNamespace}
 |])
@@ -57,12 +59,12 @@       where
         generics = angles $ sepBy ", " paramName declParams
 
-        methodDeclaration Function{..} = [lt|global::System.Threading.Tasks.Task<global::Bond.Comm.IMessage<#{getMessageResultTypeName}>> #{methodName}Async(global::Bond.Comm.IMessage<#{getMessageInputTypeName}> param, global::System.Threading.CancellationToken ct);|]
+        methodDeclaration Function{..} = [lt|#{CS.schemaAttributes 2 methodAttributes}global::System.Threading.Tasks.Task<global::Bond.Comm.IMessage<#{getMessageResultTypeName}>> #{methodName}Async(global::Bond.Comm.IMessage<#{getMessageInputTypeName}> param, global::System.Threading.CancellationToken ct);|]
           where
             getMessageResultTypeName = getMessageTypeName cs methodResult
             getMessageInputTypeName = getMessageTypeName cs methodInput
 
-        methodDeclaration Event{..} = [lt|void #{methodName}Async(global::Bond.Comm.IMessage<#{getMessageInputTypeName}> param);|]
+        methodDeclaration Event{..} = [lt|#{CS.schemaAttributes 2 methodAttributes}void #{methodName}Async(global::Bond.Comm.IMessage<#{getMessageInputTypeName}> param);|]
           where
             getMessageInputTypeName = getMessageTypeName cs methodInput
 
@@ -74,6 +76,8 @@ #{CS.disableReSharperWarnings}
 namespace #{csNamespace}
 {
+    using System.Collections.Generic;
+
     #{doubleLineSep 1 comm declarations}
 } // #{csNamespace}
 |])
@@ -103,7 +107,7 @@         interfaceGenerics = angles $ sepBy "," paramName declParams -- of the form "<T1, T2, T3>"
         proxyGenerics = sepEndBy ", " paramName declParams -- of the form "T1, T2, T3, "
 
-        proxyMethod Function{..} = [lt|public global::System.Threading.Tasks.Task<global::Bond.Comm.IMessage<#{getMessageResultTypeName}>> #{methodName}Async(#{getMessageProxyInputParam cs methodInput})
+        proxyMethod Function{..} = [lt|#{CS.schemaAttributes 2 methodAttributes}public global::System.Threading.Tasks.Task<global::Bond.Comm.IMessage<#{getMessageResultTypeName}>> #{methodName}Async(#{getMessageProxyInputParam cs methodInput})
         {
             var message = new global::Bond.Comm.Message<#{getMessageInputTypeName}>(#{paramOrBondVoid methodInput});
             return #{methodName}Async(message, global::System.Threading.CancellationToken.None);
@@ -121,7 +125,7 @@             getMessageResultTypeName = getMessageTypeName cs methodResult
             getMessageInputTypeName = getMessageTypeName cs methodInput
 
-        proxyMethod Event{..} = [lt|public void #{methodName}Async(#{getMessageProxyInputParam cs methodInput})
+        proxyMethod Event{..} = [lt|#{CS.schemaAttributes 2 methodAttributes}public void #{methodName}Async(#{getMessageProxyInputParam cs methodInput})
         {
             var message = new global::Bond.Comm.Message<#{getMessageInputTypeName}>(#{paramOrBondVoid methodInput});
             #{methodName}Async(message);
@@ -145,6 +149,8 @@ #{CS.disableReSharperWarnings}
 namespace #{csNamespace}
 {
+    using System.Collections.Generic;
+
     #{doubleLineSep 1 comm declarations}
 } // #{csNamespace}
 |])
@@ -173,7 +179,7 @@         methodInfo Function{..} = [lt|yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="#{getDeclTypeName idl s}.#{methodName}", Callback = #{methodName}Async_Glue, CallbackType = global::Bond.Comm.ServiceCallbackType.RequestResponse};|]
         methodInfo Event{..} = [lt|yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="#{getDeclTypeName idl s}.#{methodName}", Callback = #{methodName}Async_Glue, CallbackType = global::Bond.Comm.ServiceCallbackType.Event};|]
 
-        methodAbstract Function{..} = [lt|public abstract global::System.Threading.Tasks.Task<global::Bond.Comm.IMessage<#{getMessageResultTypeName}>> #{methodName}Async(global::Bond.Comm.IMessage<#{getMessageInputTypeName}> param, global::System.Threading.CancellationToken ct);|]
+        methodAbstract Function{..} = [lt|#{CS.schemaAttributes 2 methodAttributes}public abstract global::System.Threading.Tasks.Task<global::Bond.Comm.IMessage<#{getMessageResultTypeName}>> #{methodName}Async(global::Bond.Comm.IMessage<#{getMessageInputTypeName}> param, global::System.Threading.CancellationToken ct);|]
           where
             getMessageResultTypeName = getMessageTypeName cs methodResult
             getMessageInputTypeName = getMessageTypeName cs methodInput
@@ -192,7 +198,7 @@             getMessageResultTypeName = getMessageTypeName cs methodResult
             getMessageInputTypeName = getMessageTypeName cs methodInput
 
-        methodGlue Event{..} = [lt|private global::System.Threading.Tasks.Task #{methodName}Async_Glue(global::Bond.Comm.IMessage param, global::Bond.Comm.ReceiveContext context, global::System.Threading.CancellationToken ct)
+        methodGlue Event{..} = [lt|#{CS.schemaAttributes 2 methodAttributes}private global::System.Threading.Tasks.Task #{methodName}Async_Glue(global::Bond.Comm.IMessage param, global::Bond.Comm.ReceiveContext context, global::System.Threading.CancellationToken ct)
         {
             #{methodName}Async(param.Convert<#{getMessageInputTypeName}>());
             return global::Bond.Comm.CodegenHelpers.CompletedTask;
src/Language/Bond/Codegen/Cs/Grpc_cs.hs view
@@ -28,6 +28,8 @@ 
 namespace #{csNamespace}
 {
+    using System.Collections.Generic;
+
     #{doubleLineSep 1 grpc declarations}
 } // #{csNamespace}
 |])
@@ -103,9 +105,9 @@             global::Bond.Grpc.Marshaller<#{getMessageTypeName methodInput}>.Instance,
             global::Bond.Grpc.Marshaller<#{getMessageTypeName Nothing}>.Instance);|]
 
-        serverBaseMethods Function{..} = [lt|public abstract global::System.Threading.Tasks.Task<global::Bond.Grpc.IMessage<#{getMessageTypeName methodResult}>> #{methodName}(global::Bond.Grpc.IMessage<#{getMessageTypeName methodInput}> request, global::Grpc.Core.ServerCallContext context);|]
+        serverBaseMethods Function{..} = [lt|#{CS.schemaAttributes 3 methodAttributes}public abstract global::System.Threading.Tasks.Task<global::Bond.Grpc.IMessage<#{getMessageTypeName methodResult}>> #{methodName}(global::Bond.Grpc.IMessage<#{getMessageTypeName methodInput}> request, global::Grpc.Core.ServerCallContext context);|]
 
-        serverBaseMethods Event{..} = [lt|public abstract global::System.Threading.Tasks.Task #{methodName}(global::Bond.Grpc.IMessage<#{getMessageTypeName methodInput}> request, global::Grpc.Core.ServerCallContext context);
+        serverBaseMethods Event{..} = [lt|#{CS.schemaAttributes 3 methodAttributes}public abstract global::System.Threading.Tasks.Task #{methodName}(global::Bond.Grpc.IMessage<#{getMessageTypeName methodInput}> request, global::Grpc.Core.ServerCallContext context);
 
             internal global::System.Threading.Tasks.Task<global::Bond.Grpc.IMessage<#{getMessageTypeName Nothing}>> #{uniqImplName methodName}(global::Bond.Grpc.IMessage<#{getMessageTypeName methodInput}> request, global::Grpc.Core.ServerCallContext context) {
                 return global::Bond.Grpc.Internal.NothingCallHandler.Handle(#{methodName}, request, context);
@@ -117,7 +119,7 @@         requestOrVoidConstructor Nothing = [lt|global::Bond.Grpc.Message.Void|]
         requestOrVoidConstructor _ = [lt|global::Bond.Grpc.Message.From(request)|]
 
-        clientMethods Function{..} = [lt|public virtual global::Grpc.Core.AsyncUnaryCall<global::Bond.Grpc.IMessage<#{getMessageTypeName methodResult}>> #{methodName}Async(#{firstParam methodInput}global::Grpc.Core.Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+        clientMethods Function{..} = [lt|#{CS.schemaAttributes 3 methodAttributes}public virtual global::Grpc.Core.AsyncUnaryCall<global::Bond.Grpc.IMessage<#{getMessageTypeName methodResult}>> #{methodName}Async(#{firstParam methodInput}global::Grpc.Core.Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
             {
                 var message = #{requestOrVoidConstructor methodInput};
                 return #{methodName}Async(message, new global::Grpc.Core.CallOptions(headers, deadline, cancellationToken));
@@ -128,7 +130,7 @@                 return CallInvoker.AsyncUnaryCall(Method_#{methodName}, null, options, request);
             }|]
 
-        clientMethods Event{..} = [lt|public virtual void #{methodName}Async(#{firstParam methodInput}global::Grpc.Core.Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
+        clientMethods Event{..} = [lt|#{CS.schemaAttributes 3 methodAttributes}public virtual void #{methodName}Async(#{firstParam methodInput}global::Grpc.Core.Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken))
             {
                 var message = #{requestOrVoidConstructor methodInput};
                 #{methodName}Async(message, new global::Grpc.Core.CallOptions(headers, deadline, cancellationToken));
src/Language/Bond/Codegen/Cs/Util.hs view
@@ -75,7 +75,9 @@  <> generatedCodeAttr
 
 -- C# service attributes
-typeAttributes _ Service {..} = generatedCodeAttr
+typeAttributes cs s@Service {..} =
+    optionalTypeAttributes cs s
+    <> generatedCodeAttr
 
 typeAttributes _ _ = error "typeAttributes: impossible happened."
 
src/Language/Bond/Codegen/Templates.hs view
@@ -45,6 +45,7 @@     , comm_h
     , comm_cpp
     , grpc_h
+    , grpc_cpp
       -- ** C#
     , FieldMapping(..)
     , StructMapping(..)
@@ -65,6 +66,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_cpp
 import Language.Bond.Codegen.Cpp.Grpc_h
 import Language.Bond.Codegen.Cs.Types_cs
 import Language.Bond.Codegen.Cs.Comm_cs
src/Language/Bond/Codegen/TypeMapping.hs view
@@ -365,8 +365,8 @@ 
 -- C++ type mapping with custom allocator
 cppTypeCustomAlloc :: Builder -> Type -> TypeNameBuilder
-cppTypeCustomAlloc alloc BT_String = pure $ "std::basic_string<char, std::char_traits<char>, typename " <> alloc <> "::rebind<char>::other>"
-cppTypeCustomAlloc alloc BT_WString = pure $ "std::basic_string<wchar_t, std::char_traits<wchar_t>, typename " <> alloc <>  "::rebind<wchar_t>::other>"
+cppTypeCustomAlloc alloc BT_String = pure $ "std::basic_string<char, std::char_traits<char>, typename std::allocator_traits<" <> alloc <> ">::template rebind_alloc<char> >"
+cppTypeCustomAlloc alloc BT_WString = pure $ "std::basic_string<wchar_t, std::char_traits<wchar_t>, typename std::allocator_traits<" <> alloc <>  ">::template rebind_alloc<wchar_t> >"
 cppTypeCustomAlloc alloc BT_MetaName = cppTypeCustomAlloc alloc BT_String
 cppTypeCustomAlloc alloc BT_MetaFullName = cppTypeCustomAlloc alloc BT_String
 cppTypeCustomAlloc alloc (BT_List element) = "std::list<" <>> elementTypeName element <<>> ", " <>> allocator alloc element <<> ">"
@@ -382,11 +382,11 @@ 
 allocator :: Builder -> Type -> TypeNameBuilder
 allocator alloc element =
-    "typename " <>> alloc <>> "::rebind<" <>> elementTypeName element <<> ">::other"
+    "typename std::allocator_traits<" <>> alloc <>> ">::template rebind_alloc<" <>> elementTypeName element <<> ">"
 
 pairAllocator :: Builder -> Type -> Type -> TypeNameBuilder
 pairAllocator alloc key value =
-    "typename " <>> alloc <>> "::rebind<" <>> "std::pair<const " <>> elementTypeName key <<>> ", " <>> elementTypeName value <<> "> >::other"
+    "typename std::allocator_traits<" <>> alloc <>> ">::template rebind_alloc<" <>> "std::pair<const " <>> elementTypeName key <<>> ", " <>> elementTypeName value <<> "> >"
 
 cppSyntaxFix :: Builder -> Builder
 cppSyntaxFix = fromLazyText . snd . L.foldr fixInvalid (' ', mempty) . toLazyText
tests/TestMain.hs view
@@ -171,6 +171,10 @@                     [ "c#"
                     ]
                     "generic_service"
+                , verifyCsCommCodegen
+                    [ "c#"
+                    ]
+                    "service_attributes"
                 ]
             ]
         ]
tests/Tests/Codegen.hs view
@@ -96,6 +96,7 @@     options = processOptions args
     templates =
         [ grpc_h (export_attribute options)
+        , grpc_cpp
         , types_cpp
         ]