如果在C ++中将成员添加到类中,则会导致编译时错误的技术

时间:2017-10-10 14:31:31

标签: c++ reflection

我们假设您有一些包含多个公共成员的POD。您还有一些类可以从各种来源序列化和反序列化这些对象。除了单元测试之外,是否有任何编码技术可以确保在向类添加成员时更新任何需要了解添加字段的其他代码。

即。我喜欢一种会导致编译时错误的技术,直到更新所有代码来处理新字段。

3 个答案:

答案 0 :(得分:1)

C ++中没有任何反映。

但是一个解决方案,它具有吸引人的特性,它不会污染你的编译代码,是使用static_assert(sizeof(YourType) == x, <message>) x是一个硬编码常量,取决于你的编译器。

添加到YourType的所有成员都将更改sizeof,并导致编译时失败。

可能是某些情况,如果新成员占用之前结构填充结束的空间,则该功能不起作用。首先尝试使用编译器查看该方法如果这是可行的。)

答案 1 :(得分:1)

来自 c ++ 17 ,您可以利用结构化绑定:您可以在代码库中添加一个约定,只要函数需要POD 最新版本, 函数应通过结构化绑定访问pod:

struct A { int a,b,c; };

void foo( A& the_pod )
{
  auto& [a,b,c] = the_pod;

  // ... use a,b,c
}

将成员添加到A将使代码不再编译...

请注意,依赖于已建议的A的sizeof可能会在更改编译器时破坏代码(假设您的函数依赖于A(如序列化代码)恰好是可移植的,这很糟糕)

此外,错误会告诉您某些内容是错误的,而此解决方案也会告诉您 错误...

唯一的缺点我认为它依赖于程序员约定(但是,假设A只是一个增长的POD也是一个惯例...)

答案 2 :(得分:1)

在c ++ 11中,如果它们是DefaultConstructible(注释[1]),你可以完全反映默认对齐的AggregateTypes;仅构造反射id非DC。这将是棘手的,但草图并不那么难(信用:magic_get)。

  1. 注意这些类型的ctor具有所有类型的类,并且没有其他ctor采用更多参数。因此,你可以写:
  2. template<typename T, typename... Args>
    using constructed_from = decltype(T{std::forward<Args>(std::declval<Args>())...});
    
    1. 你可以写(谢谢,安东尼)
    2. template<size_t i>
      struct Ubiq {
          template<typename T>
          operator T() const; /* undef'd */
      };
      
      1. 如果您不熟悉is_detected,请阅读文档和示例:它允许您检测表达式是否有效。在这里你将测试T是否可以从N Ubiq实例构造,即,它是否至少有N个参数。
      2. 此时,您已做好准备:您只需要检查它是否可以从您预期的类型构建,而不是从您的类型和Ubiq中构建。

        如果您想要完全反思,请添加constexpr size_t ctor_param_cnt(),然后实施visit_ctor() template<typename T, size_t i> struct Generator;operator FieldType() const将通过#include <string> #include <type_traits> struct S1 { int i; std::string j; }; struct S2 { int i; std::string j; int k; }; template<typename T, typename... Args> using constructed_from = typename std::decay<decltype( T{ std::forward<Args>(std::declval<Args>())... } )>::type; template<size_t i> struct Ubiq { template<typename T> operator T() const; /* undef'd */ }; template<typename... Ts> struct make_void { typedef void type;}; template<typename... Ts> using void_t = typename make_void<Ts...>::type; template<typename T, typename AlwaysVoid, typename... Args> struct is_constructible_from : std::false_type {}; template<typename T, typename... Args> struct is_constructible_from<T, void_t<constructed_from<T, Args...>>, Args...> : std::true_type {}; static_assert( is_constructible_from<S1, void, int, std::string>::value, ""); static_assert(!is_constructible_from<S1, void, int, std::string, Ubiq<0>>::value, ""); // note: ! static_assert( is_constructible_from<S2, void, int, std::string>::value, ""); static_assert( is_constructible_from<S2, void, int, std::string, Ubiq<0>>::value, ""); // note: no ! 生成每个字段(通常为{1}}一个模板)。对于读访问,您需要存储字段偏移[1]。

        [1]请注意,这需要一个ABI,其中struct的布局仅取决于其中的类型。安腾ABI满足它。

        示例(针对您的用例):

        const gulp = require("gulp");
        const filter = require('gulp-filter');
        const fs = require('fs');
        //  const path = require('path');  not needed for this example
        const someImageMin = require('  your image min plugin   ' );
        
        
        gulp.task('default', function () {
        
          // include only files > 200KB
          // retore option needed if you want to reinject the filtered-out files later
          const myFilter = filter(file => (fs.statSync(file.path).size > 200000), {restore: true});
        
          return gulp.src(['pics/*jpg' , 'pics/*png'])
            .pipe(myFilter)
            .pipe(someImageMin())
        
             // if you want to reinject the files excluded by the filter
            .pipe(myFilter.restore)
            .pipe(gulp.dest('new'));
        });
        
        [gulp-ignore][2] is another option.