GEODE-8562: Adds new C++ test for using a class as a key (#714)

* Add new ClassAsKey test
* Use new classId for PositionKey (resolves conflict with existing classId)
* Make hashcode matche Java side hashcode
* Convert to C++11 style constructors (use explicit and default)
* Remove unneccessary headers
* Add explicit to constructor.
* Don't use c-style casts
* Improved variable names

Co-authored-by: Jacob Barrett <jabarrett@vmware.com>
diff --git a/clicache/integration-test2/SerializationTests.cs b/clicache/integration-test2/SerializationTests.cs
index bce822d..cf066f8 100644
--- a/clicache/integration-test2/SerializationTests.cs
+++ b/clicache/integration-test2/SerializationTests.cs
@@ -835,7 +835,7 @@
 
                 var cache = cluster.CreateCache();
 
-                cache.TypeRegistry.RegisterType(PositionKey.CreateDeserializable, 77);
+                cache.TypeRegistry.RegisterType(PositionKey.CreateDeserializable, 21);
                 cache.TypeRegistry.RegisterType(Position.CreateDeserializable, 22);
 
                 var region = cache.CreateRegionFactory(RegionShortcut.PROXY)
@@ -896,7 +896,7 @@
 
 				var cache = cluster.CreateCache();
 
-				cache.TypeRegistry.RegisterType(PositionKey.CreateDeserializable, 77);
+				cache.TypeRegistry.RegisterType(PositionKey.CreateDeserializable, 21);
 				cache.TypeRegistry.RegisterType(TestClassA.CreateDeserializable, 100);
 				cache.TypeRegistry.RegisterType(TestClassB.CreateDeserializable, 101);
 				cache.TypeRegistry.RegisterType(TestClassC.CreateDeserializable, 102);
diff --git a/cppcache/integration/test/CMakeLists.txt b/cppcache/integration/test/CMakeLists.txt
index e7b1d4a..3f1029b 100644
--- a/cppcache/integration/test/CMakeLists.txt
+++ b/cppcache/integration/test/CMakeLists.txt
@@ -36,6 +36,10 @@
   PdxSerializerTest.cpp
   Order.cpp
   Order.hpp
+  Position.cpp
+  Position.hpp
+  PositionKey.cpp
+  PositionKey.hpp
   RegionGetAllTest.cpp
   RegionPutAllTest.cpp
   RegionPutGetAllTest.cpp
diff --git a/cppcache/integration/test/DataSerializableTest.cpp b/cppcache/integration/test/DataSerializableTest.cpp
index e413369..77e514a 100644
--- a/cppcache/integration/test/DataSerializableTest.cpp
+++ b/cppcache/integration/test/DataSerializableTest.cpp
@@ -15,6 +15,7 @@
  * limitations under the License.
  */
 
+#include <list>
 #include <thread>
 
 #include <gtest/gtest.h>
@@ -22,19 +23,23 @@
 #include <geode/DataInput.hpp>
 #include <geode/DataOutput.hpp>
 #include <geode/DataSerializable.hpp>
+#include <geode/FunctionService.hpp>
 #include <geode/RegionFactory.hpp>
 #include <geode/RegionShortcut.hpp>
 #include <geode/TypeRegistry.hpp>
 
+#include "Position.hpp"
+#include "PositionKey.hpp"
 #include "framework/Cluster.h"
 
-namespace {
+namespace DataSerializableTest {
 
 using apache::geode::client::CacheableString;
 using apache::geode::client::CacheableStringArray;
 using apache::geode::client::DataInput;
 using apache::geode::client::DataOutput;
 using apache::geode::client::DataSerializable;
+using apache::geode::client::FunctionService;
 using apache::geode::client::RegionShortcut;
 
 class Simple : public DataSerializable {
@@ -162,4 +167,62 @@
               returnedArray->operator[](index)->toString());
   }
 }
-}  // namespace
+
+TEST(DataSerializableTest, ClassAsKey) {
+  Cluster cluster{LocatorCount{1}, ServerCount{1}};
+
+  cluster.start();
+
+  cluster.getGfsh()
+      .create()
+      .region()
+      .withName("region")
+      .withType("PARTITION")
+      .execute();
+
+  cluster.getGfsh()
+      .deploy()
+      .jar(getFrameworkString(FrameworkVariable::JavaObjectJarPath))
+      .execute();
+
+  cluster.getGfsh()
+      .executeFunction()
+      .withId("InstantiateDataSerializable")
+      .withMember("DataSerializableTest_ClassAsKey_server_0")
+      .execute();
+
+  auto cache = cluster.createCache();
+  auto region = cache.createRegionFactory(RegionShortcut::PROXY)
+                    .setPoolName("default")
+                    .create("region");
+
+  cache.getTypeRegistry().registerType(PositionKey::createDeserializable, 21);
+  cache.getTypeRegistry().registerType(Position::createDeserializable, 22);
+
+  auto key1 = std::make_shared<PositionKey>(1000);
+  auto key2 = std::make_shared<PositionKey>(1000000);
+  auto key3 = std::make_shared<PositionKey>(1000000000);
+
+  auto pos1 = std::make_shared<Position>("GOOG", 23);
+  auto pos2 = std::make_shared<Position>("IBM", 37);
+  auto pos3 = std::make_shared<Position>("PVTL", 101);
+
+  region->put(key1, pos1);
+  region->put(key2, pos2);
+  region->put(key3, pos3);
+
+  auto res1 = std::dynamic_pointer_cast<Position>(region->get(key1));
+  auto res2 = std::dynamic_pointer_cast<Position>(region->get(key2));
+  auto res3 = std::dynamic_pointer_cast<Position>(region->get(key3));
+
+  EXPECT_EQ(res1->getSecurityId(), pos1->getSecurityId());
+  EXPECT_EQ(res1->getSharesOutstanding(), pos1->getSharesOutstanding());
+
+  EXPECT_EQ(res2->getSecurityId(), pos2->getSecurityId());
+  EXPECT_EQ(res2->getSharesOutstanding(), pos2->getSharesOutstanding());
+
+  EXPECT_EQ(res3->getSecurityId(), pos3->getSecurityId());
+  EXPECT_EQ(res3->getSharesOutstanding(), pos3->getSharesOutstanding());
+}
+
+}  // namespace DataSerializableTest
diff --git a/cppcache/integration/test/Position.cpp b/cppcache/integration/test/Position.cpp
new file mode 100644
index 0000000..4cde01e
--- /dev/null
+++ b/cppcache/integration/test/Position.cpp
@@ -0,0 +1,85 @@
+/*
+ * 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 "Position.hpp"
+
+#include <geode/DataInput.hpp>
+#include <geode/DataOutput.hpp>
+
+namespace DataSerializableTest {
+
+int32_t Position::count = 0;
+
+Position::Position()
+    : volumeAverageOver20Days_(0),
+      conversionRatio_(0.0),
+      valueGain_(0.0),
+      industry_(0),
+      issuer_(0),
+      marketValue_(0.0),
+      quantity_(0.0),
+      sharesOutstanding_(0),
+      volatility_(0),
+      positionId_(0) {}
+
+Position::Position(std::string id, int32_t outstandingShares) : Position() {
+  securityId_ = std::move(id);
+  securityType_ = "a";
+  sharesOutstanding_ = outstandingShares;
+  quantity_ = outstandingShares - (count % 2 == 0 ? 1000 : 100);
+  marketValue_ = quantity_ * 1.2345998;
+  positionId_ = count++;
+}
+
+void Position::toData(apache::geode::client::DataOutput& output) const {
+  output.writeInt(volumeAverageOver20Days_);
+  output.writeString(bondRating_);
+  output.writeDouble(conversionRatio_);
+  output.writeString(country_);
+  output.writeDouble(valueGain_);
+  output.writeInt(industry_);
+  output.writeInt(issuer_);
+  output.writeDouble(marketValue_);
+  output.writeDouble(quantity_);
+  output.writeString(securityId_);
+  output.writeString(securityLinks_);
+  output.writeUTF(securityType_);
+  output.writeInt(sharesOutstanding_);
+  output.writeString(underlyingSecurity_);
+  output.writeInt(volatility_);
+  output.writeInt(positionId_);
+}
+
+void Position::fromData(apache::geode::client::DataInput& input) {
+  volumeAverageOver20Days_ = input.readInt64();
+  bondRating_ = input.readString();
+  conversionRatio_ = input.readDouble();
+  country_ = input.readString();
+  valueGain_ = input.readDouble();
+  industry_ = input.readInt64();
+  issuer_ = input.readInt64();
+  marketValue_ = input.readDouble();
+  quantity_ = input.readDouble();
+  securityId_ = input.readString();
+  securityLinks_ = input.readString();
+  securityType_ = input.readUTF();
+  sharesOutstanding_ = input.readInt32();
+  underlyingSecurity_ = input.readString();
+  volatility_ = input.readInt64();
+  positionId_ = input.readInt32();
+}
+
+}  // namespace DataSerializableTest
diff --git a/cppcache/integration/test/Position.hpp b/cppcache/integration/test/Position.hpp
new file mode 100644
index 0000000..fe188f3
--- /dev/null
+++ b/cppcache/integration/test/Position.hpp
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#ifndef POSITION_H
+#define POSITION_H
+
+/*
+ * @brief User class for testing the put functionality for object.
+ */
+
+#include <string>
+
+#include <geode/CacheableString.hpp>
+#include <geode/DataSerializable.hpp>
+
+namespace DataSerializableTest {
+
+using apache::geode::client::CacheableString;
+using apache::geode::client::DataInput;
+using apache::geode::client::DataOutput;
+using apache::geode::client::DataSerializable;
+
+class Position : public DataSerializable {
+ private:
+  int64_t volumeAverageOver20Days_;
+  std::string bondRating_;
+  double conversionRatio_;
+  std::string country_;
+  double valueGain_;
+  int64_t industry_;
+  int64_t issuer_;
+  double marketValue_;
+  double quantity_;
+  std::string securityId_;
+  std::string securityLinks_;
+  std::string securityType_;
+  int32_t sharesOutstanding_;
+  std::string underlyingSecurity_;
+  int64_t volatility_;
+  int32_t positionId_;
+
+ public:
+  static int32_t count;
+
+  Position();
+  explicit Position(std::string id, int32_t out);
+  ~Position() override = default;
+  void toData(DataOutput& output) const override;
+  void fromData(DataInput& input) override;
+
+  static void resetCounter() { count = 0; }
+  std::string getSecurityId() { return securityId_; }
+  int32_t getPOsitionId() { return positionId_; }
+  int32_t getSharesOutstanding() { return sharesOutstanding_; }
+  static std::shared_ptr<Serializable> createDeserializable() {
+    return std::make_shared<Position>();
+  }
+};
+
+}  // namespace DataSerializableTest
+
+#endif  // POSITION_H
diff --git a/cppcache/integration/test/PositionKey.cpp b/cppcache/integration/test/PositionKey.cpp
new file mode 100644
index 0000000..67df75e
--- /dev/null
+++ b/cppcache/integration/test/PositionKey.cpp
@@ -0,0 +1,43 @@
+/*
+ * 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 "PositionKey.hpp"
+
+#include <geode/DataInput.hpp>
+#include <geode/DataOutput.hpp>
+
+namespace DataSerializableTest {
+
+void PositionKey::toData(DataOutput& output) const {
+  output.writeInt(positionId_);
+}
+
+void PositionKey::fromData(apache::geode::client::DataInput& input) {
+  positionId_ = input.readInt64();
+}
+
+bool PositionKey::operator==(const CacheableKey& other) const {
+  return positionId_ ==
+         (static_cast<const PositionKey&>(other)).getPositionId();
+}
+
+int PositionKey::hashcode() const {
+  int prime = 31;
+  int result = prime * static_cast<int32_t>(positionId_);
+  return result;
+}
+
+}  // namespace DataSerializableTest
diff --git a/cppcache/integration/test/PositionKey.hpp b/cppcache/integration/test/PositionKey.hpp
new file mode 100644
index 0000000..ecc932f
--- /dev/null
+++ b/cppcache/integration/test/PositionKey.hpp
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#ifndef POSITIONKEY_H_
+#define POSITIONKEY_H_
+
+#include <string>
+
+#include <geode/CacheableString.hpp>
+#include <geode/DataSerializable.hpp>
+
+namespace DataSerializableTest {
+
+using apache::geode::client::CacheableKey;
+using apache::geode::client::DataInput;
+using apache::geode::client::DataOutput;
+using apache::geode::client::DataSerializable;
+
+class PositionKey : public DataSerializable, public CacheableKey {
+ private:
+  int64_t positionId_;
+
+ public:
+  PositionKey() = default;
+  explicit PositionKey(int64_t positionId) : positionId_(positionId) {}
+  ~PositionKey() override = default;
+
+  bool operator==(const CacheableKey& other) const override;
+  int32_t hashcode() const override;
+
+  void toData(DataOutput& output) const override;
+  void fromData(DataInput& input) override;
+
+  int64_t getPositionId() const { return positionId_; }
+  static std::shared_ptr<Serializable> createDeserializable() {
+    return std::make_shared<PositionKey>();
+  }
+};
+
+}  // namespace DataSerializableTest
+
+#endif  // POSITIONKEY_H_
diff --git a/tests/javaobject/cli/InstantiateDataSerializable.java b/tests/javaobject/cli/InstantiateDataSerializable.java
index 14b7c1f..96db447 100644
--- a/tests/javaobject/cli/InstantiateDataSerializable.java
+++ b/tests/javaobject/cli/InstantiateDataSerializable.java
@@ -32,15 +32,16 @@
 public class InstantiateDataSerializable extends FunctionAdapter implements Declarable{
 
 public void execute(FunctionContext context) {
-  Instantiator.register(new Instantiator(javaobject.cli.Position.class, 22) {
+
+  Instantiator.register(new Instantiator(javaobject.cli.PositionKey.class, 21) {
     public DataSerializable newInstance() {
-      return new javaobject.cli.Position();
+      return new javaobject.cli.PositionKey();
     }
   });
 
-  Instantiator.register(new Instantiator(javaobject.cli.PositionKey.class, 77) {
+  Instantiator.register(new Instantiator(javaobject.cli.Position.class, 22) {
     public DataSerializable newInstance() {
-      return new javaobject.cli.PositionKey();
+      return new javaobject.cli.Position();
     }
   });
 
diff --git a/tests/javaobject/cli/PositionKey.java b/tests/javaobject/cli/PositionKey.java
index b0ece8e..2a0ce2b 100644
--- a/tests/javaobject/cli/PositionKey.java
+++ b/tests/javaobject/cli/PositionKey.java
@@ -26,7 +26,7 @@
   private long positionId;
 
   static {
-     Instantiator.register(new Instantiator(javaobject.cli.PositionKey.class, (byte) 77) {
+     Instantiator.register(new Instantiator(javaobject.cli.PositionKey.class, 21) {
      public DataSerializable newInstance() {
         return new PositionKey();
      }