IGNITE-15015 CPP: Add support for affinity fields

This closes #9370
diff --git a/docs/_docs/code-snippets/cpp/src/affinity_collocation.cpp b/docs/_docs/code-snippets/cpp/src/affinity_collocation.cpp
new file mode 100644
index 0000000..175e60e
--- /dev/null
+++ b/docs/_docs/code-snippets/cpp/src/affinity_collocation.cpp
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include "ignite/ignite.h"
+#include "ignite/ignition.h"
+
+
+//tag::affinity-collocation[]
+struct Person
+{
+    int32_t id;
+    std::string name;
+    int32_t cityId;
+    std::string companyId;
+};
+
+struct PersonKey
+{
+    int32_t id;
+    std::string companyId;
+};
+
+struct Company
+{
+    std::string name;
+};
+
+namespace ignite { namespace binary {
+template<> struct BinaryType<Person> : BinaryTypeDefaultAll<Person>
+{
+    static void GetTypeName(std::string& dst)
+    {
+        dst = "Person";
+    }
+
+    static void Write(BinaryWriter& writer, const Person& obj)
+    {
+        writer.WriteInt32("id", obj.id);
+        writer.WriteString("name", obj.name);
+        writer.WriteInt32("cityId", obj.cityId);
+        writer.WriteString("companyId", obj.companyId);
+    }
+
+    static void Read(BinaryReader& reader, Person& dst)
+    {
+        dst.id = reader.ReadInt32("id");
+        dst.name = reader.ReadString("name");
+        dst.cityId = reader.ReadInt32("cityId");
+        dst.companyId = reader.ReadString("companyId");
+    }
+};
+
+template<> struct BinaryType<PersonKey> : BinaryTypeDefaultAll<PersonKey>
+{
+    static void GetTypeName(std::string& dst)
+    {
+        dst = "PersonKey";
+    }
+
+    static void GetAffinityFieldName(std::string& dst)
+    {
+        dst = "companyId";
+    }
+
+    static void Write(BinaryWriter& writer, const PersonKey& obj)
+    {
+        writer.WriteInt32("id", obj.id);
+        writer.WriteString("companyId", obj.companyId);
+    }
+
+    static void Read(BinaryReader& reader, PersonKey& dst)
+    {
+        dst.id = reader.ReadInt32("id");
+        dst.companyId = reader.ReadString("companyId");
+    }
+};
+
+template<> struct BinaryType<Company> : BinaryTypeDefaultAll<Company>
+{
+    static void GetTypeName(std::string& dst)
+    {
+        dst = "Company";
+    }
+
+    static void Write(BinaryWriter& writer, const Company& obj)
+    {
+        writer.WriteString("name", obj.name);
+    }
+
+    static void Read(BinaryReader& reader, Company& dst)
+    {
+        dst.name = reader.ReadString("name");
+    }
+};
+}};  // namespace ignite::binary
+
+int main()
+{
+    using namespace ignite;
+    using namespace cache;
+
+    IgniteConfiguration cfg;
+    Ignite ignite = Ignition::Start(cfg);
+
+    Cache<PersonKey, Person> personCache = ignite.GetOrCreateCache<PersonKey, Person>("person");
+    Cache<std::string, Company> companyCache = ignite.GetOrCreateCache<std::string, Company>("company");
+
+    Person person{};
+    person.name = "Vasya";
+
+    Company company{};
+    company.name = "Company1";
+
+    personCache.Put(PersonKey{1, "company1_key"}, person);
+    companyCache.Put("company1_key", company);
+
+    return 0;
+}
+//end::affinity-collocation[]
diff --git a/docs/_docs/data-modeling/affinity-collocation.adoc b/docs/_docs/data-modeling/affinity-collocation.adoc
index e6576fa..f7591d1 100644
--- a/docs/_docs/data-modeling/affinity-collocation.adoc
+++ b/docs/_docs/data-modeling/affinity-collocation.adoc
@@ -54,6 +54,7 @@
 
 :javaSourceFile: {javaCodeDir}/AffinityCollocationExample.java
 :dotnetSourceFile: code-snippets/dotnet/AffinityCollocation.cs
+:cppSourceFile: code-snippets/cpp/src/affinity_collocation.cpp
 
 [tabs]
 --
@@ -67,7 +68,11 @@
 ----
 include::{dotnetSourceFile}[tag=affinityCollocation,indent=0]
 ----
-tab:C++[unsupported]
+tab:C++[]
+[source,cpp]
+----
+include::{cppSourceFile}[tag=affinity-collocation,indent=0]
+----
 
 tab:SQL[]
 [source,sql]
diff --git a/modules/core/src/test/java/org/apache/ignite/platform/PlatformComputePutAffinityKeyTask.java b/modules/core/src/test/java/org/apache/ignite/platform/PlatformComputePutAffinityKeyTask.java
new file mode 100644
index 0000000..e384ec5
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/platform/PlatformComputePutAffinityKeyTask.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.platform;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.compute.ComputeJob;
+import org.apache.ignite.compute.ComputeJobAdapter;
+import org.apache.ignite.compute.ComputeJobResult;
+import org.apache.ignite.compute.ComputeTaskAdapter;
+import org.apache.ignite.internal.binary.AffinityKey;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.resources.IgniteInstanceResource;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/** Test task that basically puts value to cache on Java side. */
+public class PlatformComputePutAffinityKeyTask extends ComputeTaskAdapter<Object, Object> {
+    /** {@inheritDoc} */
+    @NotNull @Override public Map<? extends ComputeJob, ClusterNode> map(List<ClusterNode> subgrid,
+        @Nullable Object arg) {
+        return Collections.singletonMap(new PutJob(), F.first(subgrid));
+    }
+
+    /** {@inheritDoc} */
+    @Nullable @Override public Object reduce(List<ComputeJobResult> results) {
+        return results.get(0).getData();
+    }
+
+    /** Job. */
+    private static class PutJob extends ComputeJobAdapter {
+        @IgniteInstanceResource
+        private Ignite ignite;
+
+        /** {@inheritDoc} */
+        @Nullable @Override public Object execute() {
+            AffinityKey val = new AffinityKey(1, 2);
+            ignite.getOrCreateCache("default").put(val, val);
+
+            return null;
+        }
+    }
+}
diff --git a/modules/platforms/cpp/binary/include/ignite/impl/binary/binary_reader_impl.h b/modules/platforms/cpp/binary/include/ignite/impl/binary/binary_reader_impl.h
index b165d8e..801b882 100644
--- a/modules/platforms/cpp/binary/include/ignite/impl/binary/binary_reader_impl.h
+++ b/modules/platforms/cpp/binary/include/ignite/impl/binary/binary_reader_impl.h
@@ -949,7 +949,7 @@
                 template<typename T>
                 T ReadTopObject()
                 {
-                    return ignite::binary::ReadHelper<T>::Read(*this);
+                    return ReadHelper<T>::Read(*this);
                 }
 
                 /**
@@ -960,7 +960,7 @@
                 template<typename T>
                 void ReadTopObject(T& res)
                 {
-                    return ignite::binary::ReadHelper<T>::Read(*this, res);
+                    return ReadHelper<T>::Read(*this, res);
                 }
 
                 /**
diff --git a/modules/platforms/cpp/binary/include/ignite/impl/binary/binary_type_impl.h b/modules/platforms/cpp/binary/include/ignite/impl/binary/binary_type_impl.h
index 8ed43c0..a38e6f3 100644
--- a/modules/platforms/cpp/binary/include/ignite/impl/binary/binary_type_impl.h
+++ b/modules/platforms/cpp/binary/include/ignite/impl/binary/binary_type_impl.h
@@ -23,6 +23,31 @@
 
 #include <ignite/ignite_error.h>
 
+#include <ignite/common/utils.h>
+
+/**
+ * Some SFINAE magic to check existence of the specified method with the
+ * specified signature in the BinaryType<T> class.
+ *
+ * This macro declares checker for the method.
+ */
+#define IGNITE_DECLARE_BINARY_TYPE_METHOD_CHECKER(method, sign)                         \
+    template<typename T>                                                                \
+    class IsDeclaredBinaryType##method                                                  \
+    {                                                                                   \
+        typedef char one;                                                               \
+        typedef char two[2];                                                            \
+                                                                                        \
+        template<class U, U> struct test;                                               \
+                                                                                        \
+        template<typename C> static one& helper(test<sign, &C::method>*);               \
+        template<typename C> static two& helper(...);                                   \
+                                                                                        \
+    public:                                                                             \
+        const static bool value =                                                       \
+            (sizeof(helper< ignite::binary::BinaryType<T> >(0)) == sizeof(one));        \
+    }
+
 namespace ignite
 {
     namespace binary
@@ -56,85 +81,161 @@
 
             static void Read(BinaryReader& reader, IgniteError& dst);
         };
+    } // namespace binary
 
-        /**
-         * Write helper. Takes care of proper writing of pointers.
-         */
-        template<typename T>
-        struct WriteHelper
+    namespace impl
+    {
+        namespace binary
         {
-            template<typename W>
-            static void Write(W& writer, const T& val)
+            /**
+             * Write helper. Takes care of proper writing of pointers.
+             */
+            template<typename T>
+            struct WriteHelper
             {
-                writer.template WriteTopObject0<BinaryWriter>(val);
-            }
-        };
+                template<typename W>
+                static void Write(W& writer, const T& val)
+                {
+                    writer.template WriteTopObject0<ignite::binary::BinaryWriter>(val);
+                }
+            };
 
-        /**
-         * Specialization for the pointer case.
-         */
-        template<typename T>
-        struct WriteHelper<T*>
-        {
-            template<typename W>
-            static void Write(W& writer, const T* val)
+            /**
+             * Specialization for the pointer case.
+             */
+            template<typename T>
+            struct WriteHelper<T*>
             {
-                if (!val)
-                    writer.WriteNull0();
-                else
-                    writer.template WriteTopObject0<BinaryWriter>(*val);
-            }
-        };
+                template<typename W>
+                static void Write(W& writer, const T* val)
+                {
+                    if (!val)
+                        writer.WriteNull0();
+                    else
+                        writer.template WriteTopObject0<ignite::binary::BinaryWriter>(*val);
+                }
+            };
 
-        /**
-         * Read helper. Takes care of proper reading of pointers.
-         */
-        template<typename T>
-        struct ReadHelper
-        {
-            template<typename R>
-            static T Read(R& reader)
+            /**
+             * Read helper. Takes care of proper reading of pointers.
+             */
+            template<typename T>
+            struct ReadHelper
             {
-                T res;
+                template<typename R>
+                static T Read(R& reader)
+                {
+                    T res;
 
-                Read<R>(reader, res);
+                    Read<R>(reader, res);
 
-                return res;
-            }
+                    return res;
+                }
 
-            template<typename R>
-            static void Read(R& reader, T& val)
+                template<typename R>
+                static void Read(R& reader, T& val)
+                {
+                    reader.template ReadTopObject0<ignite::binary::BinaryReader, T>(val);
+                }
+            };
+
+            /**
+             * Specialization for the pointer case.
+             */
+            template<typename T>
+            struct ReadHelper<T*>
             {
-                reader.template ReadTopObject0<BinaryReader, T>(val);
-            }
-        };
+                template<typename R>
+                static T* Read(R& reader)
+                {
+                    if (reader.SkipIfNull())
+                        return 0;
 
-        /**
-         * Specialization for the pointer case.
-         */
-        template<typename T>
-        struct ReadHelper<T*>
-        {
-            template<typename R>
-            static T* Read(R& reader)
+                    std::auto_ptr<T> res(new T());
+
+                    reader.template ReadTopObject0<ignite::binary::BinaryReader, T>(*res);
+
+                    return res.release();
+                }
+
+                template<typename R>
+                static void Read(R& reader, T*& ptr)
+                {
+                    ptr = Read<R>(reader);
+                }
+            };
+
+            IGNITE_DECLARE_BINARY_TYPE_METHOD_CHECKER(GetAffinityFieldName, void(*)(std::string&));
+
+            /**
+             * This type is used to get affinity field name for binary types which have not GetAffinityFieldName
+             * method defined.
+             */
+            template<typename T>
+            struct AffinityFieldNameGetterDefault
             {
-                if (reader.SkipIfNull())
-                    return 0;
+                static void Get(std::string& affField)
+                {
+                    affField.clear();
+                }
+            };
 
-                std::auto_ptr<T> res(new T());
-
-                reader.template ReadTopObject0<BinaryReader, T>(*res);
-
-                return res.release();
-            }
-
-            template<typename R>
-            static void Read(R& reader, T*& ptr)
+            /**
+             * This type is used to get affinity field name for binary types which have GetAffinityFieldName
+             * method defined.
+             */
+            template<typename T>
+            struct AffinityFieldNameGetterMethod
             {
-                ptr = Read<R>(reader);
+                static void Get(std::string& affField)
+                {
+                    ignite::binary::BinaryType<T>::GetAffinityFieldName(affField);
+                }
+            };
+
+            /**
+             * This type is used to get affinity field name for pointers to binary types which have GetAffinityFieldName
+             * method defined.
+             */
+            template<typename T>
+            struct AffinityFieldNameGetterMethod<T*>
+            {
+                static void Get(std::string& affField)
+                {
+                    ignite::binary::BinaryType<T>::GetAffinityFieldName(affField);
+                }
+            };
+
+            /**
+             * Get affinity field name for the specified object.
+             * Determines the best method to use based on user-defined methods.
+             *
+             * @param affField Affinity field name.
+             * @return Affinity field name.
+             */
+            template<typename T>
+            void GetAffinityFieldName(std::string& affField)
+            {
+                using namespace common;
+
+                // Choosing right getter to use.
+                typedef typename Conditional<
+                    // Checking if the BinaryType<T>::GetAffinityFieldName declared
+                    IsDeclaredBinaryTypeGetAffinityFieldName<T>::value,
+
+                    // True case. Using user-provided method.
+                    AffinityFieldNameGetterMethod<T>,
+
+                    // False case. Using default implementation.
+                    AffinityFieldNameGetterDefault<T>
+
+                >::type AffinityFieldNameGetter;
+
+                // Call itself.
+                AffinityFieldNameGetter::Get(affField);
             }
-        };
-    }
-}
+        } // namespace binary
+    } // namespace impl
+} // namespace ignite
 
 #endif //_IGNITE_IMPL_BINARY_BINARY_TYPE_IMPL
diff --git a/modules/platforms/cpp/binary/include/ignite/impl/binary/binary_type_manager.h b/modules/platforms/cpp/binary/include/ignite/impl/binary/binary_type_manager.h
index 0568b92..c1cbb8d 100644
--- a/modules/platforms/cpp/binary/include/ignite/impl/binary/binary_type_manager.h
+++ b/modules/platforms/cpp/binary/include/ignite/impl/binary/binary_type_manager.h
@@ -50,9 +50,11 @@
                  * Get handler.
                  *
                  * @param typeName Type name.
+                 * @param affFieldName Affinity field name.
                  * @param typeId Type ID.
                  */
-                common::concurrent::SharedPointer<BinaryTypeHandler> GetHandler(const std::string& typeName, int32_t typeId);
+                common::concurrent::SharedPointer<BinaryTypeHandler> GetHandler(const std::string& typeName,
+                    const std::string& affFieldName, int32_t typeId);
 
                 /**
                  * Submit handler for processing.
diff --git a/modules/platforms/cpp/binary/include/ignite/impl/binary/binary_type_snapshot.h b/modules/platforms/cpp/binary/include/ignite/impl/binary/binary_type_snapshot.h
index 136b33b..c14c014 100644
--- a/modules/platforms/cpp/binary/include/ignite/impl/binary/binary_type_snapshot.h
+++ b/modules/platforms/cpp/binary/include/ignite/impl/binary/binary_type_snapshot.h
@@ -46,9 +46,11 @@
                  * Constructor.
                  *
                  * @param typeName Type name.
+                 * @param affFieldName Affinity fiend name.
                  * @param typeId Type ID.
                  */
-                IGNITE_IMPORT_EXPORT BinaryTypeSnapshot(std::string typeName, int32_t typeId);
+                IGNITE_IMPORT_EXPORT BinaryTypeSnapshot(const std::string& typeName, const std::string& affFieldName,
+                    int32_t typeId);
 
                 /**
                  * Copy constructor.
@@ -79,6 +81,16 @@
                 }
 
                 /**
+                 * Get affinity field name.
+                 *
+                 * @return Affinity field name.
+                 */
+                const std::string& GetAffinityFieldName() const
+                {
+                    return affFieldName;
+                }
+
+                /**
                  * Get type ID.
                  *
                  * @return Type ID.
@@ -141,6 +153,9 @@
                 /** Type name. */
                 std::string typeName;
 
+                /** Affinity field name. */
+                std::string affFieldName;
+
                 /** Type ID. */
                 int32_t typeId;
 
diff --git a/modules/platforms/cpp/binary/include/ignite/impl/binary/binary_writer_impl.h b/modules/platforms/cpp/binary/include/ignite/impl/binary/binary_writer_impl.h
index 5efbe47..c9b923a 100644
--- a/modules/platforms/cpp/binary/include/ignite/impl/binary/binary_writer_impl.h
+++ b/modules/platforms/cpp/binary/include/ignite/impl/binary/binary_writer_impl.h
@@ -776,7 +776,7 @@
                 template<typename T>
                 void WriteTopObject(const T& obj)
                 {
-                    ignite::binary::WriteHelper<T>::Write(*this, obj);
+                    WriteHelper<T>::Write(*this, obj);
                 }
 
                 /**
@@ -800,8 +800,11 @@
                         std::string typeName;
                         BType::GetTypeName(typeName);
 
+                        std::string affField;
+                        GetAffinityFieldName<T>(affField);
+
                         if (metaMgr)
-                            metaHnd = metaMgr->GetHandler(typeName, idRslvr.GetTypeId());
+                            metaHnd = metaMgr->GetHandler(typeName, affField, idRslvr.GetTypeId());
 
                         int32_t pos = stream->Position();
 
@@ -815,7 +818,7 @@
 
                         int32_t hashPos = stream->Reserve(4);
 
-                        // Reserve space for the Object Lenght, Schema ID and Schema or Raw Offset.
+                        // Reserve space for the Object Length, Schema ID and Schema or Raw Offset.
                         stream->Reserve(12);
 
                         BType::Write(writer, obj);
diff --git a/modules/platforms/cpp/binary/src/impl/binary/binary_type_manager.cpp b/modules/platforms/cpp/binary/src/impl/binary/binary_type_manager.cpp
index e127914..70ee47a 100644
--- a/modules/platforms/cpp/binary/src/impl/binary/binary_type_manager.cpp
+++ b/modules/platforms/cpp/binary/src/impl/binary/binary_type_manager.cpp
@@ -48,7 +48,8 @@
                 delete pending;
             }
 
-            SharedPointer<BinaryTypeHandler> BinaryTypeManager::GetHandler(const std::string& typeName, int32_t typeId)
+            SharedPointer<BinaryTypeHandler> BinaryTypeManager::GetHandler(const std::string& typeName,
+                const std::string& affFieldName, int32_t typeId)
             {
                 { // Locking scope.
                     CsLockGuard guard(cs);
@@ -66,7 +67,7 @@
                     }
                 }
 
-                SPSnap snapshot = SPSnap(new Snap(typeName ,typeId));
+                SPSnap snapshot = SPSnap(new Snap(typeName, affFieldName, typeId));
 
                 return SharedPointer<BinaryTypeHandler>(new BinaryTypeHandler(snapshot));
             }
diff --git a/modules/platforms/cpp/binary/src/impl/binary/binary_type_snapshot.cpp b/modules/platforms/cpp/binary/src/impl/binary/binary_type_snapshot.cpp
index 2a9928e..f5a3956 100644
--- a/modules/platforms/cpp/binary/src/impl/binary/binary_type_snapshot.cpp
+++ b/modules/platforms/cpp/binary/src/impl/binary/binary_type_snapshot.cpp
@@ -23,8 +23,10 @@
     {
         namespace binary
         {
-            IGNITE_IMPORT_EXPORT BinaryTypeSnapshot::BinaryTypeSnapshot(std::string typeName, int32_t typeId) :
+            IGNITE_IMPORT_EXPORT BinaryTypeSnapshot::BinaryTypeSnapshot(const std::string& typeName,
+                const std::string& affFieldName, int32_t typeId) :
                 typeName(typeName),
+                affFieldName(affFieldName),
                 typeId(typeId),
                 fieldIds(),
                 fields()
@@ -34,6 +36,7 @@
 
             BinaryTypeSnapshot::BinaryTypeSnapshot(const BinaryTypeSnapshot& another) :
                 typeName(another.typeName),
+                affFieldName(another.affFieldName),
                 typeId(another.typeId),
                 fieldIds(another.fieldIds),
                 fields(another.fields)
diff --git a/modules/platforms/cpp/core-test/config/interop-32.xml b/modules/platforms/cpp/core-test/config/interop-32.xml
new file mode 100644
index 0000000..5724fa7
--- /dev/null
+++ b/modules/platforms/cpp/core-test/config/interop-32.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<!--
+    Ignite Spring configuration file to startup grid cache.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xsi:schemaLocation="
+        http://www.springframework.org/schema/beans
+        http://www.springframework.org/schema/beans/spring-beans.xsd
+        http://www.springframework.org/schema/util
+        http://www.springframework.org/schema/util/spring-util.xsd">
+    <import resource="interop-default.xml"/>
+
+    <bean parent="grid.cfg">
+        <property name="memoryConfiguration">
+            <bean class="org.apache.ignite.configuration.MemoryConfiguration">
+                <property name="systemCacheInitialSize" value="#{10 * 1024 * 1024}"/>
+                <property name="systemCacheMaxSize" value="#{40 * 1024 * 1024}"/>
+                <property name="defaultMemoryPolicyName" value="dfltPlc"/>
+
+                <property name="memoryPolicies">
+                    <list>
+                        <bean class="org.apache.ignite.configuration.MemoryPolicyConfiguration">
+                            <property name="name" value="dfltPlc"/>
+                            <property name="maxSize" value="#{100 * 1024 * 1024}"/>
+                            <property name="initialSize" value="#{10 * 1024 * 1024}"/>
+                        </bean>
+                    </list>
+                </property>
+            </bean>
+        </property>
+    </bean>
+</beans>
diff --git a/modules/platforms/cpp/core-test/config/interop-default.xml b/modules/platforms/cpp/core-test/config/interop-default.xml
new file mode 100644
index 0000000..19a510c
--- /dev/null
+++ b/modules/platforms/cpp/core-test/config/interop-default.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<!--
+    Ignite Spring configuration file to startup grid cache.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xsi:schemaLocation="
+        http://www.springframework.org/schema/beans
+        http://www.springframework.org/schema/beans/spring-beans.xsd
+        http://www.springframework.org/schema/util
+        http://www.springframework.org/schema/util/spring-util.xsd">
+    <bean abstract="true" id="grid.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
+        <property name="localHost" value="127.0.0.1"/>
+        <property name="connectorConfiguration"><null/></property>
+
+        <property name="binaryConfiguration">
+            <bean class="org.apache.ignite.configuration.BinaryConfiguration">
+                <property name="compactFooter" value="false"/>
+
+                <property name="idMapper">
+                    <bean class="org.apache.ignite.binary.BinaryBasicIdMapper">
+                        <property name="lowerCase" value="true"/>
+                    </bean>
+                </property>
+
+                <property name="nameMapper">
+                    <bean class="org.apache.ignite.binary.BinaryBasicNameMapper">
+                        <property name="simpleName" value="true"/>
+                    </bean>
+                </property>
+            </bean>
+        </property>
+
+        <property name="discoverySpi">
+            <bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi">
+                <property name="ipFinder">
+                    <bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder">
+                        <property name="addresses">
+                            <list>
+                                <!-- In distributed environment, replace with actual host IP address. -->
+                                <value>127.0.0.1:47500..47503</value>
+                            </list>
+                        </property>
+                    </bean>
+                </property>
+                <property name="socketTimeout" value="300" />
+            </bean>
+        </property>
+    </bean>
+</beans>
diff --git a/modules/platforms/cpp/core-test/config/interop.xml b/modules/platforms/cpp/core-test/config/interop.xml
new file mode 100644
index 0000000..da10457
--- /dev/null
+++ b/modules/platforms/cpp/core-test/config/interop.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<!--
+    Ignite Spring configuration file to startup grid cache.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xsi:schemaLocation="
+        http://www.springframework.org/schema/beans
+        http://www.springframework.org/schema/beans/spring-beans.xsd
+        http://www.springframework.org/schema/util
+        http://www.springframework.org/schema/util/spring-util.xsd">
+    <import resource="interop-default.xml"/>
+
+    <bean parent="grid.cfg"/>
+
+</beans>
diff --git a/modules/platforms/cpp/core-test/include/ignite/test_utils.h b/modules/platforms/cpp/core-test/include/ignite/test_utils.h
index 2d6bec8..4fba28e 100644
--- a/modules/platforms/cpp/core-test/include/ignite/test_utils.h
+++ b/modules/platforms/cpp/core-test/include/ignite/test_utils.h
@@ -106,6 +106,14 @@
     ignite::Ignite StartNode(const char* cfgFile, const char* name);
 
     /**
+     * Start node with the config for the current platform.
+     *
+     * @param cfg Basic config path. Changed to platform config if needed.
+     * @param name Instance name.
+     */
+    ignite::Ignite StartPlatformNode(const char* cfg, const char* name);
+
+    /**
      * Remove all the LFS artifacts.
      */
     void ClearLfs();
diff --git a/modules/platforms/cpp/core-test/project/vs/core-test.vcxproj b/modules/platforms/cpp/core-test/project/vs/core-test.vcxproj
index 7f6f5ee..d1a9e62 100644
--- a/modules/platforms/cpp/core-test/project/vs/core-test.vcxproj
+++ b/modules/platforms/cpp/core-test/project/vs/core-test.vcxproj
@@ -33,28 +33,43 @@
     </ProjectReference>
   </ItemGroup>
   <ItemGroup>
-    <None Include="..\..\config\cache-identity.xml" />
-    <None Include="..\..\config\cache-identity-32.xml" />
-    <None Include="..\..\config\cache-identity-default.xml" />
-    <None Include="..\..\config\cache-query.xml" />
-    <None Include="..\..\config\cache-query-32.xml" />
-    <None Include="..\..\config\cache-query-default.xml" />
-    <None Include="..\..\config\cache-store.xml" />
-    <None Include="..\..\config\cache-store-32.xml" />
-    <None Include="..\..\config\cache-store-default.xml" />
-    <None Include="..\..\config\cache-test.xml" />
-    <None Include="..\..\config\cache-test-32.xml" />
-    <None Include="..\..\config\cache-test-default.xml" />
-    <None Include="..\..\config\cache-query-continuous.xml" />
-    <None Include="..\..\config\cache-query-continuous-32.xml" />
-    <None Include="..\..\config\cache-query-continuous-default.xml" />
-    <None Include="..\..\config\isolated.xml" />
-    <None Include="..\..\config\isolated-32.xml" />
-    <None Include="..\..\config\isolated-default.xml" />
-    <None Include="..\..\config\persistence-store.xml" />
-    <None Include="..\..\config\persistence-store-32.xml" />
-    <None Include="..\..\config\persistence-store-default.xml" />
-    <None Include="..\..\config\invalid.xml" />
+    <Xml Include="..\..\config\cache-identity.xml" />
+    <Xml Include="..\..\config\cache-identity-32.xml" />
+    <Xml Include="..\..\config\cache-identity-default.xml" />
+    <Xml Include="..\..\config\cache-query.xml" />
+    <Xml Include="..\..\config\cache-query-32.xml" />
+    <Xml Include="..\..\config\cache-query-default.xml" />
+    <Xml Include="..\..\config\cache-store.xml" />
+    <Xml Include="..\..\config\cache-store-32.xml" />
+    <Xml Include="..\..\config\cache-store-default.xml" />
+    <Xml Include="..\..\config\cache-test.xml" />
+    <Xml Include="..\..\config\cache-test-32.xml" />
+    <Xml Include="..\..\config\cache-test-default.xml" />
+    <Xml Include="..\..\config\cache-query-continuous.xml" />
+    <Xml Include="..\..\config\cache-query-continuous-32.xml" />
+    <Xml Include="..\..\config\cache-query-continuous-default.xml" />
+    <Xml Include="..\..\config\isolated.xml" />
+    <Xml Include="..\..\config\isolated-32.xml" />
+    <Xml Include="..\..\config\isolated-default.xml" />
+    <Xml Include="..\..\config\persistence-store.xml" />
+    <Xml Include="..\..\config\persistence-store-32.xml" />
+    <Xml Include="..\..\config\persistence-store-default.xml" />
+    <Xml Include="..\..\config\invalid.xml" />
+    <Xml Include="..\..\config\interop.xml" />
+    <Xml Include="..\..\config\interop-32.xml" />
+    <Xml Include="..\..\config\interop-default.xml" />
+    <Xml Include="..\..\config\cache-native-persistence-test-32.xml" />
+    <Xml Include="..\..\config\cache-native-persistence-test-default.xml" />
+    <Xml Include="..\..\config\cache-native-persistence-test.xml" />
+    <Xml Include="..\..\config\compute-client-32.xml" />
+    <Xml Include="..\..\config\compute-client-default.xml" />
+    <Xml Include="..\..\config\compute-client.xml" />
+    <Xml Include="..\..\config\compute-server0-32.xml" />
+    <Xml Include="..\..\config\compute-server0-default.xml" />
+    <Xml Include="..\..\config\compute-server0.xml" />
+    <Xml Include="..\..\config\compute-server1-32.xml" />
+    <Xml Include="..\..\config\compute-server1-default.xml" />
+    <Xml Include="..\..\config\compute-server1.xml" />
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\src\affinity_test.cpp" />
@@ -100,20 +115,6 @@
     <ClInclude Include="..\..\include\ignite\test_utils.h" />
     <ClInclude Include="..\..\include\teamcity_messages.h" />
   </ItemGroup>
-  <ItemGroup>
-    <Xml Include="..\..\config\cache-native-persistence-test-32.xml" />
-    <Xml Include="..\..\config\cache-native-persistence-test-default.xml" />
-    <Xml Include="..\..\config\cache-native-persistence-test.xml" />
-    <Xml Include="..\..\config\compute-client-32.xml" />
-    <Xml Include="..\..\config\compute-client-default.xml" />
-    <Xml Include="..\..\config\compute-client.xml" />
-    <Xml Include="..\..\config\compute-server0-32.xml" />
-    <Xml Include="..\..\config\compute-server0-default.xml" />
-    <Xml Include="..\..\config\compute-server0.xml" />
-    <Xml Include="..\..\config\compute-server1-32.xml" />
-    <Xml Include="..\..\config\compute-server1-default.xml" />
-    <Xml Include="..\..\config\compute-server1.xml" />
-  </ItemGroup>
   <PropertyGroup Label="Globals">
     <ProjectGuid>{133A22DB-FD60-44B9-B5E3-6CBB3EA5ABF0}</ProjectGuid>
     <RootNamespace>coretest</RootNamespace>
diff --git a/modules/platforms/cpp/core-test/project/vs/core-test.vcxproj.filters b/modules/platforms/cpp/core-test/project/vs/core-test.vcxproj.filters
index e73d2c0..bac4262 100644
--- a/modules/platforms/cpp/core-test/project/vs/core-test.vcxproj.filters
+++ b/modules/platforms/cpp/core-test/project/vs/core-test.vcxproj.filters
@@ -145,74 +145,81 @@
     </Filter>
   </ItemGroup>
   <ItemGroup>
-    <None Include="..\..\config\invalid.xml">
+    <Xml Include="..\..\config\invalid.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\cache-test.xml">
+    </Xml>
+    <Xml Include="..\..\config\interop.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\cache-test-32.xml">
+    </Xml>
+    <Xml Include="..\..\config\interop-32.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\cache-test-default.xml">
+    </Xml>
+    <Xml Include="..\..\config\interop-default.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\cache-query.xml">
+    </Xml>
+    <Xml Include="..\..\config\cache-test.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\cache-query-32.xml">
+    </Xml>
+    <Xml Include="..\..\config\cache-test-32.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\cache-query-default.xml">
+    </Xml>
+    <Xml Include="..\..\config\cache-test-default.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\cache-query-continuous.xml">
+    </Xml>
+    <Xml Include="..\..\config\cache-query.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\cache-query-continuous-32.xml">
+    </Xml>
+    <Xml Include="..\..\config\cache-query-32.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\cache-query-continuous-default.xml">
+    </Xml>
+    <Xml Include="..\..\config\cache-query-default.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\cache-identity.xml">
+    </Xml>
+    <Xml Include="..\..\config\cache-query-continuous.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\cache-identity-32.xml">
+    </Xml>
+    <Xml Include="..\..\config\cache-query-continuous-32.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\cache-identity-default.xml">
+    </Xml>
+    <Xml Include="..\..\config\cache-query-continuous-default.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\cache-store.xml">
+    </Xml>
+    <Xml Include="..\..\config\cache-identity.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\cache-store-32.xml">
+    </Xml>
+    <Xml Include="..\..\config\cache-identity-32.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\cache-store-default.xml">
+    </Xml>
+    <Xml Include="..\..\config\cache-identity-default.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\isolated.xml">
+    </Xml>
+    <Xml Include="..\..\config\cache-store.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\isolated-32.xml">
+    </Xml>
+    <Xml Include="..\..\config\cache-store-32.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\isolated-default.xml">
+    </Xml>
+    <Xml Include="..\..\config\cache-store-default.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\persistence-store.xml">
+    </Xml>
+    <Xml Include="..\..\config\isolated.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\persistence-store-32.xml">
+    </Xml>
+    <Xml Include="..\..\config\isolated-32.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\persistence-store-default.xml">
+    </Xml>
+    <Xml Include="..\..\config\isolated-default.xml">
       <Filter>Configs</Filter>
-    </None>
-  </ItemGroup>
-  <ItemGroup>
+    </Xml>
+    <Xml Include="..\..\config\persistence-store.xml">
+      <Filter>Configs</Filter>
+    </Xml>
+    <Xml Include="..\..\config\persistence-store-32.xml">
+      <Filter>Configs</Filter>
+    </Xml>
+    <Xml Include="..\..\config\persistence-store-default.xml">
+      <Filter>Configs</Filter>
+    </Xml>
     <Xml Include="..\..\config\compute-client.xml">
       <Filter>Configs</Filter>
     </Xml>
diff --git a/modules/platforms/cpp/core-test/src/interop_test.cpp b/modules/platforms/cpp/core-test/src/interop_test.cpp
index 6a7ad06..17a291f 100644
--- a/modules/platforms/cpp/core-test/src/interop_test.cpp
+++ b/modules/platforms/cpp/core-test/src/interop_test.cpp
@@ -24,7 +24,94 @@
 using namespace cache;
 using namespace boost::unit_test;
 
-BOOST_AUTO_TEST_SUITE(InteropTestSuite)
+namespace
+{
+    /** Test put affinity key Java task. */
+    const std::string TEST_PUT_AFFINITY_KEY_TASK("org.apache.ignite.platform.PlatformComputePutAffinityKeyTask");
+}
+
+class InteropTestSuiteFixture
+{
+public:
+    InteropTestSuiteFixture()
+    {
+        // No-op.
+    }
+
+    ~InteropTestSuiteFixture()
+    {
+        ignite::Ignition::StopAll(false);
+    }
+};
+
+
+/**
+ * Affinity key class.
+ */
+struct AffinityKey
+{
+    /** Key */
+    int32_t key;
+
+    /** Affinity key */
+    int32_t aff;
+
+    /**
+     * Default constructor.
+     */
+    AffinityKey() :
+            key(0),
+            aff(0)
+    {
+        // No-op.
+    }
+
+    /**
+     * Constructor.
+     * @param key Key.
+     * @param aff Affinity key.
+     */
+    AffinityKey(int32_t key, int32_t aff) :
+            key(key),
+            aff(aff)
+    {
+        // No-op.
+    }
+};
+
+namespace ignite
+{
+    namespace binary
+    {
+        template<>
+        struct BinaryType<AffinityKey> : BinaryTypeDefaultAll<AffinityKey>
+        {
+            static void GetTypeName(std::string& dst)
+            {
+                dst = "AffinityKey";
+            }
+
+            static void Write(BinaryWriter& writer, const AffinityKey& obj)
+            {
+                writer.WriteInt32("key", obj.key);
+                writer.WriteInt32("aff", obj.aff);
+            }
+
+            static void Read(BinaryReader& reader, AffinityKey& dst)
+            {
+                dst.key = reader.ReadInt32("key");
+                dst.aff = reader.ReadInt32("aff");
+            }
+
+            static void GetAffinityFieldName(std::string& dst)
+            {
+                dst = "aff";
+            }
+        };
+    }
+}
+
+BOOST_FIXTURE_TEST_SUITE(InteropTestSuite, InteropTestSuiteFixture)
 
 #ifdef ENABLE_STRING_SERIALIZATION_VER_2_TESTS
 
@@ -85,11 +172,7 @@
 
 BOOST_AUTO_TEST_CASE(StringUtfValid4ByteCodePoint)
 {
-#ifdef IGNITE_TESTS_32
-    Ignite ignite = ignite_test::StartNode("cache-test-32.xml");
-#else
-    Ignite ignite = ignite_test::StartNode("cache-test.xml");
-#endif
+    Ignite ignite = ignite_test::StartPlatformNode("cache-test.xml", "ServerNode");
 
     Cache<std::string, std::string> cache = ignite.CreateCache<std::string, std::string>("Test");
 
@@ -113,4 +196,50 @@
     Ignition::StopAll(false);
 }
 
+BOOST_AUTO_TEST_CASE(PutObjectByCppThenByJava)
+{
+    Ignite ignite = ignite_test::StartPlatformNode("interop.xml", "ServerNode");
+
+    cache::Cache<AffinityKey, AffinityKey> cache = ignite.GetOrCreateCache<AffinityKey, AffinityKey>("default");
+
+    AffinityKey key1(2, 3);
+    cache.Put(key1, key1);
+
+    compute::Compute compute = ignite.GetCompute();
+
+    compute.ExecuteJavaTask<int*>(TEST_PUT_AFFINITY_KEY_TASK);
+
+    AffinityKey key2(1, 2);
+    AffinityKey val = cache.Get(key2);
+
+    BOOST_CHECK_EQUAL(val.key, 1);
+    BOOST_CHECK_EQUAL(val.aff, 2);
+}
+
+BOOST_AUTO_TEST_CASE(PutObjectPointerByCppThenByJava)
+{
+    Ignite ignite = ignite_test::StartPlatformNode("interop.xml", "ServerNode");
+
+    cache::Cache<AffinityKey*, AffinityKey*> cache =
+            ignite.GetOrCreateCache<AffinityKey*, AffinityKey*>("default");
+
+    AffinityKey* key1 = new AffinityKey(2, 3);
+    cache.Put(key1, key1);
+
+    delete key1;
+
+    compute::Compute compute = ignite.GetCompute();
+
+    compute.ExecuteJavaTask<int*>(TEST_PUT_AFFINITY_KEY_TASK);
+
+    AffinityKey* key2 = new AffinityKey(1, 2);
+    AffinityKey* val = cache.Get(key2);
+
+    BOOST_CHECK_EQUAL(val->key, 1);
+    BOOST_CHECK_EQUAL(val->aff, 2);
+
+    delete key2;
+    delete val;
+}
+
 BOOST_AUTO_TEST_SUITE_END()
diff --git a/modules/platforms/cpp/core-test/src/test_utils.cpp b/modules/platforms/cpp/core-test/src/test_utils.cpp
index 31c0ef2..917bc62 100644
--- a/modules/platforms/cpp/core-test/src/test_utils.cpp
+++ b/modules/platforms/cpp/core-test/src/test_utils.cpp
@@ -113,6 +113,19 @@
         return Ignition::Start(cfg, name);
     }
 
+    ignite::Ignite StartPlatformNode(const char* cfg, const char* name)
+    {
+        std::string config(cfg);
+
+#ifdef IGNITE_TESTS_32
+        // Cutting off the ".xml" part.
+        config.resize(config.size() - 4);
+        config += "-32.xml";
+#endif //IGNITE_TESTS_32
+
+        return StartNode(config.c_str(), name);
+    }
+
     std::string AppendPath(const std::string& base, const std::string& toAdd)
     {
         std::stringstream stream;
diff --git a/modules/platforms/cpp/core/src/impl/binary/binary_type_updater_impl.cpp b/modules/platforms/cpp/core/src/impl/binary/binary_type_updater_impl.cpp
index 8f5274f..2cc3e2d 100644
--- a/modules/platforms/cpp/core/src/impl/binary/binary_type_updater_impl.cpp
+++ b/modules/platforms/cpp/core/src/impl/binary/binary_type_updater_impl.cpp
@@ -75,8 +75,14 @@
 
                 rawWriter.WriteInt32(snap.GetTypeId());
                 rawWriter.WriteString(snap.GetTypeName());
-                rawWriter.WriteString(0); // Affinity key is not supported for now.
-                
+
+                const std::string& affFieldName = snap.GetAffinityFieldName();
+
+                if (affFieldName.empty())
+                    rawWriter.WriteNull();
+                else
+                    rawWriter.WriteString(affFieldName);
+
                 if (snap.HasFields())
                 {
                     const Snap::FieldMap& fields = snap.GetFieldMap();
@@ -143,11 +149,9 @@
                 assert(typeId == readTypeId);
 
                 std::string typeName = rawReader.ReadString();
+                std::string affFieldName = rawReader.ReadString();
 
-                SPSnap res(new Snap(typeName, readTypeId));
-
-                // Skipping affinity key field name.
-                rawReader.ReadString();
+                SPSnap res(new Snap(typeName, affFieldName, readTypeId));
                 
                 int32_t fieldsNum = rawReader.ReadInt32();
 
diff --git a/modules/platforms/cpp/examples/include/ignite/examples/person.h b/modules/platforms/cpp/examples/include/ignite/examples/person.h
index 9e6a01a..69f2083 100644
--- a/modules/platforms/cpp/examples/include/ignite/examples/person.h
+++ b/modules/platforms/cpp/examples/include/ignite/examples/person.h
@@ -136,6 +136,11 @@
                 dst = "PersonKey";
             }
 
+            static void GetAffinityFieldName(std::string& dst)
+            {
+                dst = "orgIdAff";
+            }
+
             static void Write(BinaryWriter& writer, const examples::PersonKey& obj)
             {
                 writer.WriteInt64("id", obj.id);
diff --git a/modules/platforms/cpp/thin-client-test/CMakeLists.txt b/modules/platforms/cpp/thin-client-test/CMakeLists.txt
index fcbf128..824edd0 100644
--- a/modules/platforms/cpp/thin-client-test/CMakeLists.txt
+++ b/modules/platforms/cpp/thin-client-test/CMakeLists.txt
@@ -34,6 +34,7 @@
         src/compute_client_test.cpp
         src/test_utils.cpp
         src/ignite_client_test.cpp
+        src/interop_test.cpp
         src/sql_fields_query_test.cpp
         src/auth_test.cpp
         src/tx_test.cpp
diff --git a/modules/platforms/cpp/thin-client-test/config/interop-32.xml b/modules/platforms/cpp/thin-client-test/config/interop-32.xml
new file mode 100644
index 0000000..5724fa7
--- /dev/null
+++ b/modules/platforms/cpp/thin-client-test/config/interop-32.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<!--
+    Ignite Spring configuration file to startup grid cache.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xsi:schemaLocation="
+        http://www.springframework.org/schema/beans
+        http://www.springframework.org/schema/beans/spring-beans.xsd
+        http://www.springframework.org/schema/util
+        http://www.springframework.org/schema/util/spring-util.xsd">
+    <import resource="interop-default.xml"/>
+
+    <bean parent="grid.cfg">
+        <property name="memoryConfiguration">
+            <bean class="org.apache.ignite.configuration.MemoryConfiguration">
+                <property name="systemCacheInitialSize" value="#{10 * 1024 * 1024}"/>
+                <property name="systemCacheMaxSize" value="#{40 * 1024 * 1024}"/>
+                <property name="defaultMemoryPolicyName" value="dfltPlc"/>
+
+                <property name="memoryPolicies">
+                    <list>
+                        <bean class="org.apache.ignite.configuration.MemoryPolicyConfiguration">
+                            <property name="name" value="dfltPlc"/>
+                            <property name="maxSize" value="#{100 * 1024 * 1024}"/>
+                            <property name="initialSize" value="#{10 * 1024 * 1024}"/>
+                        </bean>
+                    </list>
+                </property>
+            </bean>
+        </property>
+    </bean>
+</beans>
diff --git a/modules/platforms/cpp/thin-client-test/config/interop-default.xml b/modules/platforms/cpp/thin-client-test/config/interop-default.xml
new file mode 100644
index 0000000..424ef4c
--- /dev/null
+++ b/modules/platforms/cpp/thin-client-test/config/interop-default.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<!--
+    Ignite Spring configuration file to startup grid cache.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xsi:schemaLocation="
+        http://www.springframework.org/schema/beans
+        http://www.springframework.org/schema/beans/spring-beans.xsd
+        http://www.springframework.org/schema/util
+        http://www.springframework.org/schema/util/spring-util.xsd">
+    <bean abstract="true" id="grid.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
+        <property name="localHost" value="127.0.0.1"/>
+        <property name="connectorConfiguration"><null/></property>
+
+        <property name="clientConnectorConfiguration">
+            <bean class="org.apache.ignite.configuration.ClientConnectorConfiguration">
+                <property name="host" value="127.0.0.1"/>
+                <property name="port" value="11110"/>
+                <property name="portRange" value="10"/>
+                <property name="thinClientConfiguration">
+                    <bean class="org.apache.ignite.configuration.ThinClientConfiguration">
+                        <property name="maxActiveComputeTasksPerConnection" value="100" />
+                    </bean>
+                </property>
+            </bean>
+        </property>
+
+        <property name="binaryConfiguration">
+            <bean class="org.apache.ignite.configuration.BinaryConfiguration">
+                <property name="compactFooter" value="false"/>
+
+                <property name="idMapper">
+                    <bean class="org.apache.ignite.binary.BinaryBasicIdMapper">
+                        <property name="lowerCase" value="true"/>
+                    </bean>
+                </property>
+
+                <property name="nameMapper">
+                    <bean class="org.apache.ignite.binary.BinaryBasicNameMapper">
+                        <property name="simpleName" value="true"/>
+                    </bean>
+                </property>
+            </bean>
+        </property>
+
+        <property name="discoverySpi">
+            <bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi">
+                <property name="ipFinder">
+                    <bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder">
+                        <property name="addresses">
+                            <list>
+                                <!-- In distributed environment, replace with actual host IP address. -->
+                                <value>127.0.0.1:47500..47503</value>
+                            </list>
+                        </property>
+                    </bean>
+                </property>
+                <property name="socketTimeout" value="300" />
+            </bean>
+        </property>
+    </bean>
+</beans>
diff --git a/modules/platforms/cpp/thin-client-test/config/interop.xml b/modules/platforms/cpp/thin-client-test/config/interop.xml
new file mode 100644
index 0000000..da10457
--- /dev/null
+++ b/modules/platforms/cpp/thin-client-test/config/interop.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<!--
+    Ignite Spring configuration file to startup grid cache.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xsi:schemaLocation="
+        http://www.springframework.org/schema/beans
+        http://www.springframework.org/schema/beans/spring-beans.xsd
+        http://www.springframework.org/schema/util
+        http://www.springframework.org/schema/util/spring-util.xsd">
+    <import resource="interop-default.xml"/>
+
+    <bean parent="grid.cfg"/>
+
+</beans>
diff --git a/modules/platforms/cpp/thin-client-test/project/vs/thin-client-test.vcxproj b/modules/platforms/cpp/thin-client-test/project/vs/thin-client-test.vcxproj
index 2b5534b..54d7411 100644
--- a/modules/platforms/cpp/thin-client-test/project/vs/thin-client-test.vcxproj
+++ b/modules/platforms/cpp/thin-client-test/project/vs/thin-client-test.vcxproj
@@ -23,6 +23,7 @@
     <ClCompile Include="..\..\src\cache_client_test.cpp" />
     <ClCompile Include="..\..\src\compute_client_test.cpp" />
     <ClCompile Include="..\..\src\ignite_client_test.cpp" />
+    <ClCompile Include="..\..\src\interop_test.cpp" />
     <ClCompile Include="..\..\src\ssl_test.cpp" />
     <ClCompile Include="..\..\src\sql_fields_query_test.cpp" />
     <ClCompile Include="..\..\src\teamcity\teamcity_boost.cpp" />
@@ -54,21 +55,27 @@
     </ProjectReference>
   </ItemGroup>
   <ItemGroup>
-    <None Include="..\..\config\auth-32.xml" />
-    <None Include="..\..\config\auth-default.xml" />
-    <None Include="..\..\config\auth.xml" />
-    <None Include="..\..\config\cache-32.xml" />
-    <None Include="..\..\config\cache-default.xml" />
-    <None Include="..\..\config\cache.xml" />
-    <None Include="..\..\config\non-ssl-32.xml" />
-    <None Include="..\..\config\non-ssl-default.xml" />
-    <None Include="..\..\config\non-ssl.xml" />
-    <None Include="..\..\config\ssl-32.xml" />
-    <None Include="..\..\config\ssl-default.xml" />
-    <None Include="..\..\config\ssl.xml" />
-    <None Include="..\..\config\sql-query-fields-32.xml" />
-    <None Include="..\..\config\sql-query-fields-default.xml" />
-    <None Include="..\..\config\sql-query-fields.xml" />
+    <Xml Include="..\..\config\auth-32.xml" />
+    <Xml Include="..\..\config\auth-default.xml" />
+    <Xml Include="..\..\config\auth.xml" />
+    <Xml Include="..\..\config\cache-32.xml" />
+    <Xml Include="..\..\config\cache-default.xml" />
+    <Xml Include="..\..\config\cache.xml" />
+    <Xml Include="..\..\config\compute-32.xml" />
+    <Xml Include="..\..\config\compute-default.xml" />
+    <Xml Include="..\..\config\compute.xml" />
+    <Xml Include="..\..\config\interop-32.xml" />
+    <Xml Include="..\..\config\interop-default.xml" />
+    <Xml Include="..\..\config\interop.xml" />
+    <Xml Include="..\..\config\non-ssl-32.xml" />
+    <Xml Include="..\..\config\non-ssl-default.xml" />
+    <Xml Include="..\..\config\non-ssl.xml" />
+    <Xml Include="..\..\config\ssl-32.xml" />
+    <Xml Include="..\..\config\ssl-default.xml" />
+    <Xml Include="..\..\config\ssl.xml" />
+    <Xml Include="..\..\config\sql-query-fields-32.xml" />
+    <Xml Include="..\..\config\sql-query-fields-default.xml" />
+    <Xml Include="..\..\config\sql-query-fields.xml" />
   </ItemGroup>
   <PropertyGroup Label="Globals">
     <ProjectGuid>{5662F10A-9C40-45D6-AFF8-E93573FEAABA}</ProjectGuid>
diff --git a/modules/platforms/cpp/thin-client-test/project/vs/thin-client-test.vcxproj.filters b/modules/platforms/cpp/thin-client-test/project/vs/thin-client-test.vcxproj.filters
index 1bde459..a8652dc 100644
--- a/modules/platforms/cpp/thin-client-test/project/vs/thin-client-test.vcxproj.filters
+++ b/modules/platforms/cpp/thin-client-test/project/vs/thin-client-test.vcxproj.filters
@@ -44,6 +44,9 @@
     <ClCompile Include="..\..\src\compute_client_test.cpp">
       <Filter>Code</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\src\interop_test.cpp">
+      <Filter>Code</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\include\teamcity\teamcity_messages.h">
@@ -60,50 +63,68 @@
     </ClInclude>
   </ItemGroup>
   <ItemGroup>
-    <None Include="..\..\config\cache.xml">
+    <Xml Include="..\..\config\cache.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\cache-32.xml">
+    </Xml>
+    <Xml Include="..\..\config\cache-32.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\cache-default.xml">
+    </Xml>
+    <Xml Include="..\..\config\cache-default.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\ssl.xml">
+    </Xml>
+    <Xml Include="..\..\config\compute.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\ssl-32.xml">
+    </Xml>
+    <Xml Include="..\..\config\compute-32.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\ssl-default.xml">
+    </Xml>
+    <Xml Include="..\..\config\compute-default.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\auth.xml">
+    </Xml>
+    <Xml Include="..\..\config\interop.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\auth-32.xml">
+    </Xml>
+    <Xml Include="..\..\config\interop-32.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\auth-default.xml">
+    </Xml>
+    <Xml Include="..\..\config\interop-default.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\non-ssl.xml">
+    </Xml>
+    <Xml Include="..\..\config\ssl.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\non-ssl-32.xml">
+    </Xml>
+    <Xml Include="..\..\config\ssl-32.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\non-ssl-default.xml">
+    </Xml>
+    <Xml Include="..\..\config\ssl-default.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\sql-query-fields.xml">
+    </Xml>
+    <Xml Include="..\..\config\auth.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\sql-query-fields-32.xml">
+    </Xml>
+    <Xml Include="..\..\config\auth-32.xml">
       <Filter>Configs</Filter>
-    </None>
-    <None Include="..\..\config\sql-query-fields-default.xml">
+    </Xml>
+    <Xml Include="..\..\config\auth-default.xml">
       <Filter>Configs</Filter>
-    </None>
+    </Xml>
+    <Xml Include="..\..\config\non-ssl.xml">
+      <Filter>Configs</Filter>
+    </Xml>
+    <Xml Include="..\..\config\non-ssl-32.xml">
+      <Filter>Configs</Filter>
+    </Xml>
+    <Xml Include="..\..\config\non-ssl-default.xml">
+      <Filter>Configs</Filter>
+    </Xml>
+    <Xml Include="..\..\config\sql-query-fields.xml">
+      <Filter>Configs</Filter>
+    </Xml>
+    <Xml Include="..\..\config\sql-query-fields-32.xml">
+      <Filter>Configs</Filter>
+    </Xml>
+    <Xml Include="..\..\config\sql-query-fields-default.xml">
+      <Filter>Configs</Filter>
+    </Xml>
   </ItemGroup>
 </Project>
\ No newline at end of file
diff --git a/modules/platforms/cpp/thin-client-test/src/interop_test.cpp b/modules/platforms/cpp/thin-client-test/src/interop_test.cpp
new file mode 100644
index 0000000..5c3959c
--- /dev/null
+++ b/modules/platforms/cpp/thin-client-test/src/interop_test.cpp
@@ -0,0 +1,178 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <boost/test/unit_test.hpp>
+
+#include <ignite/ignition.h>
+
+#include <ignite/thin/ignite_client_configuration.h>
+#include <ignite/thin/ignite_client.h>
+
+#include <ignite/complex_type.h>
+#include <test_utils.h>
+
+using namespace ignite::thin;
+using namespace boost::unit_test;
+
+namespace
+{
+    /** Test put affinity key Java task. */
+    const std::string TEST_PUT_AFFINITY_KEY_TASK("org.apache.ignite.platform.PlatformComputePutAffinityKeyTask");
+}
+
+class InteropTestSuiteFixture
+{
+public:
+    static ignite::Ignite StartNode(const char* name)
+    {
+        return ignite_test::StartCrossPlatformServerNode("interop.xml", name);
+    }
+
+    InteropTestSuiteFixture()
+    {
+        serverNode = StartNode("ServerNode");
+
+        IgniteClientConfiguration cfg;
+        cfg.SetEndPoints("127.0.0.1:11110");
+
+        client = IgniteClient::Start(cfg);
+    }
+
+    ~InteropTestSuiteFixture()
+    {
+        ignite::Ignition::StopAll(false);
+    }
+
+protected:
+    /** Server node. */
+    ignite::Ignite serverNode;
+
+    /** Client. */
+    IgniteClient client;
+};
+
+/**
+ * Affinity key class.
+ */
+struct AffinityKey
+{
+    /** Key */
+    int32_t key;
+
+    /** Affinity key */
+    int32_t aff;
+
+    /**
+     * Default constructor.
+     */
+    AffinityKey() :
+            key(0),
+            aff(0)
+    {
+        // No-op.
+    }
+
+    /**
+     * Constructor.
+     * @param key Key.
+     * @param aff Affinity key.
+     */
+    AffinityKey(int32_t key, int32_t aff) :
+            key(key),
+            aff(aff)
+    {
+        // No-op.
+    }
+};
+
+namespace ignite
+{
+    namespace binary
+    {
+        template<>
+        struct BinaryType<AffinityKey> : BinaryTypeDefaultAll<AffinityKey>
+        {
+            static void GetTypeName(std::string& dst)
+            {
+                dst = "AffinityKey";
+            }
+
+            static void Write(BinaryWriter& writer, const AffinityKey& obj)
+            {
+                writer.WriteInt32("key", obj.key);
+                writer.WriteInt32("aff", obj.aff);
+            }
+
+            static void Read(BinaryReader& reader, AffinityKey& dst)
+            {
+                dst.key = reader.ReadInt32("key");
+                dst.aff = reader.ReadInt32("aff");
+            }
+
+            static void GetAffinityFieldName(std::string& dst)
+            {
+                dst = "aff";
+            }
+        };
+    }
+}
+
+BOOST_FIXTURE_TEST_SUITE(InteropTestSuite, InteropTestSuiteFixture)
+
+BOOST_AUTO_TEST_CASE(PutObjectByCppThenByJava)
+{
+    cache::CacheClient<AffinityKey, AffinityKey> cache = client.GetOrCreateCache<AffinityKey, AffinityKey>("default");
+
+    AffinityKey key1(2, 3);
+    cache.Put(key1, key1);
+
+    compute::ComputeClient compute = client.GetCompute();
+
+    compute.ExecuteJavaTask<int*>(TEST_PUT_AFFINITY_KEY_TASK);
+
+    AffinityKey key2(1, 2);
+    AffinityKey val = cache.Get(key2);
+
+    BOOST_CHECK_EQUAL(val.key, 1);
+    BOOST_CHECK_EQUAL(val.aff, 2);
+}
+
+BOOST_AUTO_TEST_CASE(PutObjectPointerByCppThenByJava)
+{
+    cache::CacheClient<AffinityKey*, AffinityKey*> cache =
+        client.GetOrCreateCache<AffinityKey*, AffinityKey*>("default");
+
+    AffinityKey* key1 = new AffinityKey(2, 3);
+    cache.Put(key1, key1);
+
+    delete key1;
+
+    compute::ComputeClient compute = client.GetCompute();
+
+    compute.ExecuteJavaTask<int*>(TEST_PUT_AFFINITY_KEY_TASK);
+
+    AffinityKey* key2 = new AffinityKey(1, 2);
+    AffinityKey* val = cache.Get(key2);
+
+    BOOST_CHECK_EQUAL(val->key, 1);
+    BOOST_CHECK_EQUAL(val->aff, 2);
+
+    delete key2;
+    delete val;
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/modules/platforms/cpp/thin-client/src/impl/message.cpp b/modules/platforms/cpp/thin-client/src/impl/message.cpp
index b37c6e1..5bf5329 100644
--- a/modules/platforms/cpp/thin-client/src/impl/message.cpp
+++ b/modules/platforms/cpp/thin-client/src/impl/message.cpp
@@ -208,8 +208,11 @@
                 writer.WriteInt32(snapshot.GetTypeId());
                 writer.WriteString(snapshot.GetTypeName());
 
-                // Affinity Key Field name.
-                writer.WriteNull();
+                const std::string& affFieldName = snapshot.GetAffinityFieldName();
+                if (affFieldName.empty())
+                    writer.WriteNull();
+                else
+                    writer.WriteString(affFieldName);
 
                 const binary::Snap::FieldMap& fields = snapshot.GetFieldMap();
 
@@ -236,11 +239,10 @@
                 std::string typeName;
                 reader.ReadString(typeName);
 
-                // Unused for now.
-                std::string affKeyFieldNameUnused;
-                reader.ReadString(affKeyFieldNameUnused);
+                std::string affKeyFieldName;
+                reader.ReadString(affKeyFieldName);
 
-                snapshot = binary::SPSnap(new binary::Snap(typeName, typeId));
+                snapshot = binary::SPSnap(new binary::Snap(typeName, affKeyFieldName, typeId));
 
                 int32_t fieldsNum = reader.ReadInt32();