Repository: rapidsai/cudf (language: python)

Issue:
[FEA] Have `cudf::make_empty_column(cudf::type_id::STRING)` return a column with a child column of empty offsets
**Is your feature request related to a problem? Please describe.**
I am trying to add some validation in the cudf Python layer when constructing a string column, namely that a string column is always constructed with 1 child column of offsets.

I noticed that `cudf::make_empty_column(cudf::type_id::STRING)` will create a string column with no children.

For reference, the Python side "`column_empty`" equivalent will construct an empty string column with an offset child column

https://github.com/rapidsai/cudf/blob/e0d1ac1efa9153f0a084bd72b7d4c300f9640196/python/cudf/cudf/core/column/column.py#L1520-L1525

**Describe the solution you'd like**
Allowing `cudf::make_empty_column(cudf::type_id::STRING)` to create a string column with a `cudf::make_empty_column(cudf::type_id::INT32)` child column.

**Describe alternatives you've considered**
Status quo.

**Additional context**
Add any other context, code examples, or references to existing implementations about the feature request here.



Discussion / hints:
Since there is no data in the parent strings column, accessing any of the child columns (or data or nullmask) would be considered UB.
Also, this would create a new invalid state which would require even more additional checking since it is expected the a child offsets child column should contain 1+ number of elements of the parent strings column. 
OK so would you recommend the cudf Python side to not create a child column when creating an empty child column (like the snippet does above)?
Related, I also notice passing a non-empty string column to `cudf::empty_like` will create a new string column with 1, empty child column. To align then with `cudf::make_empty_column` shouldn't the new column have no children?

https://github.com/rapidsai/cudf/blob/e8156d42163fb02aa90baba9be20ab89bc9ebef1/cpp/src/copying/copy.cpp#L142-L154

Fix patch:
diff --git a/cpp/src/copying/copy.cpp b/cpp/src/copying/copy.cpp
index 98ee6aa8f68..bac8dbe5d95 100644
--- a/cpp/src/copying/copy.cpp
+++ b/cpp/src/copying/copy.cpp
@@ -143,6 +143,12 @@ std::unique_ptr<column> empty_like(column_view const& input)
 {
   CUDF_FUNC_RANGE();
 
+  // test_dataframe.py passes an EMPTY column type here;
+  // this causes is_nested to throw an error since it uses the type-dispatcher
+  if ((input.type().id() == type_id::EMPTY) || !cudf::is_nested(input.type())) {
+    return make_empty_column(input.type());
+  }
+
   std::vector<std::unique_ptr<column>> children;
   std::transform(input.child_begin(),
                  input.child_end(),


Test patch:
diff --git a/cpp/tests/copying/pack_tests.cpp b/cpp/tests/copying/pack_tests.cpp
index ea4408efa6a..8a50e071cb9 100644
--- a/cpp/tests/copying/pack_tests.cpp
+++ b/cpp/tests/copying/pack_tests.cpp
@@ -573,6 +573,8 @@ TEST_F(PackUnpackTest, SlicedEmpty)
 
   cudf::table_view t({a, b, c, d});
 
-  auto sliced = cudf::split(t, {0});
-  this->run_test(sliced[0]);
+  auto sliced   = cudf::split(t, {0});
+  auto packed   = cudf::pack(t);
+  auto unpacked = cudf::unpack(packed);
+  CUDF_TEST_EXPECT_TABLES_EQUIVALENT(t, unpacked);
 }
diff --git a/cpp/tests/replace/replace_nulls_tests.cpp b/cpp/tests/replace/replace_nulls_tests.cpp
index 9603ea44a76..fcee27305f2 100644
--- a/cpp/tests/replace/replace_nulls_tests.cpp
+++ b/cpp/tests/replace/replace_nulls_tests.cpp
@@ -674,7 +674,7 @@ TEST_F(ReplaceDictionaryTest, ReplaceNullsEmpty)
   cudf::test::fixed_width_column_wrapper<int64_t> input_empty_w({});
   auto input_empty = cudf::dictionary::encode(input_empty_w);
   auto result      = cudf::replace_nulls(input_empty->view(), input_empty->view());
-  CUDF_TEST_EXPECT_COLUMNS_EQUAL(result->view(), input_empty->view());
+  CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(result->view(), input_empty->view());
 }
 
 TEST_F(ReplaceDictionaryTest, ReplaceNullsNoNulls)