Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove libecl-style "type-safety" #4051

Merged
merged 3 commits into from
Oct 14, 2022
Merged

Remove libecl-style "type-safety" #4051

merged 3 commits into from
Oct 14, 2022

Conversation

pinkwah
Copy link
Contributor

@pinkwah pinkwah commented Oct 12, 2022

Old clib C classes provide _safe_cast, _safe_cast_const and _is_instance functions that take a void * as input and cast it to the correct function, or util_abort if the types are incorrect. This works because old-style structs start with a int __type_id (UTIL_TYPE_ID_DECLARATION macro), and this number is the magic _TYPE_ID for that type. Eg. enkf_fs_type's TYPE_ID is 1089763 -- a unique arbitrary number chosen when the type was first created. If the __type_id matches the given TYPE_ID for a type, then the _safe_cast functions pass.

There are multiple issues with this:

  1. There is no guarantee that the void * object starts with int __type_id. Running enkf_fs_safe_cast("foo") where the type is const char * is undefined behaviour. So while the code may compile for a lot of pointers, it might not be a safe operation (ie. "safe cast" isn't actually safe).
  2. C++ is a strongly-typed language. Unlike C, there are no implicit pointer casts (except to void *. Notably this is why you need to cast malloc() in C++ but not C). We should use the type system correctly and resolve any type issues at compile-time rather than run-time. "Safe" casting consumes CPU cycles when the correct solution would not.
  3. libecl's types use type-erasure to make things behave in C. For example, vector_type cannot know which type it contains, so it must rely on void * to do the job. Therefore, a lot of interaction with libecl's API is using void *. We are moving away from this approach, instead relying on C++ templated classes which do preserve the type.

@pinkwah pinkwah force-pushed the unsafe branch 5 times, most recently from 9707497 to 737b327 Compare October 12, 2022 13:13
@pinkwah pinkwah marked this pull request as ready for review October 12, 2022 13:17
@pinkwah pinkwah self-assigned this Oct 12, 2022
@@ -1,8 +1,6 @@
#ifndef ERT_SUMMARY_KEY_SET_H
#define ERT_SUMMARY_KEY_SET_H

#include <ert/util/type_macros.h>

#include <ert/enkf/enkf_types.hpp>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aparently stringlist_type was transiently imported from type_macros here. The fact that build/test succeeds means that ert/enkf/summary_key_set.hpp is never read.

Copy link
Contributor

@eivindjahren eivindjahren left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely better! The diff is so big I this is going to be war 💥, good luck with your deployment soldier 🖖 !

@eivindjahren
Copy link
Contributor

old_tests/res_util/test_ert_util_ui_return.cpp seems to not find ui_return.hpp, and after looking into it, that test neither compiled nor ran.

@pinkwah pinkwah force-pushed the unsafe branch 2 times, most recently from b2f64a3 to 9c8a87e Compare October 14, 2022 08:41
Old `clib` C classes provide `_safe_cast`, `_safe_cast_const` and `_is_instance`
functions that take a `void *` as input and cast it to the correct function, or
`util_abort` if the types are incorrect. This works because old-style structs
start with a `int __type_id` (`UTIL_TYPE_ID_DECLARATION` macro), and this number
is the magic `_TYPE_ID` for that type. Eg. `enkf_fs_type`'s TYPE_ID is `1089763`
-- a unique arbitrary number chosen when the type was first created. If the
`__type_id` matches the given TYPE_ID for a type, then the `_safe_cast`
functions pass.

There are multiple issues with this:
1. There is no guarantee that the `void *` object starts with `int __type_id`.
   Running `enkf_fs_safe_cast("foo")` where the type is `const char *` is
   undefined behaviour. So while the code may compile for a lot of pointers, it
   might not be a safe operation (ie. "safe cast" isn't actually safe).
2. C++ is a strongly-typed language. Unlike C, there are no implicit pointer
   casts (except _to_ `void *`. Notably this is why you need to cast `malloc()`
   in C++ but not C). We should use the type system correctly and resolve any
   type issues at compile-time rather than run-time. "Safe" casting consumes CPU
   cycles when the correct solution would not.
3. libecl's types use type-erasure to make things behave in C. For example,
   `vector_type` cannot know which type it contains, so it must rely on `void *`
   to do the job. Therefore, a lot of interaction with libecl's API is using
   `void *`. We are moving away from this approach, instead relying on C++
   templated classes which do preserve the type.
This is a continuation of the previous commit. I remove the
implementation rather than the uses in this.
@codecov-commenter
Copy link

Codecov Report

Merging #4051 (b2f64a3) into main (88dfe46) will increase coverage by 0.60%.
The diff coverage is 46.03%.

❗ Current head b2f64a3 differs from pull request most recent head 7bfee30. Consider uploading reports for the commit 7bfee30 to get more accurate results

@@            Coverage Diff             @@
##             main    #4051      +/-   ##
==========================================
+ Coverage   57.70%   58.31%   +0.60%     
==========================================
  Files         523      526       +3     
  Lines       39522    39161     -361     
  Branches     3592     3452     -140     
==========================================
+ Hits        22808    22837      +29     
+ Misses      15782    15445     -337     
+ Partials      932      879      -53     
Impacted Files Coverage Δ
src/clib/lib/config/config_content.cpp 75.47% <ø> (+0.31%) ⬆️
src/clib/lib/config/config_parser.cpp 65.80% <ø> (ø)
src/clib/lib/config/config_path_stack.cpp 100.00% <ø> (ø)
src/clib/lib/enkf/analysis_config.cpp 41.56% <ø> (+0.10%) ⬆️
src/clib/lib/enkf/block_fs_driver.cpp 55.33% <ø> (-0.30%) ⬇️
src/clib/lib/enkf/enkf_config_node.cpp 43.27% <ø> (+0.07%) ⬆️
src/clib/lib/enkf/enkf_fs.cpp 46.02% <ø> (+0.25%) ⬆️
src/clib/lib/enkf/enkf_obs.cpp 42.58% <ø> (-0.04%) ⬇️
src/clib/lib/enkf/enkf_plot_data.cpp 30.23% <ø> (-0.88%) ⬇️
src/clib/lib/enkf/enkf_plot_gen_kw.cpp 26.66% <ø> (ø)
... and 127 more

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

@pinkwah pinkwah merged commit f4042c5 into equinor:main Oct 14, 2022
@pinkwah pinkwah deleted the unsafe branch October 14, 2022 10:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

None yet

3 participants