草庐IT

c++ - 如何处理程序版本更新时不断变化的数据结构?

coder 2023-11-13 原文

我做嵌入式软件,但这并不是真正的嵌入式问题,我猜。我不(由于技术原因不能)使用像 MySQL 这样的数据库,只使用 C 或 C++ 结构。

是否有关于如何处理程序版本之间这些结构的布局变化的通用哲学?

让我们拿一个地址簿。从程序版本 x 到 x+1,如果:

  • 一个字段被删除(看起来很简单)或添加(如果所有人都可以使用一些新的默认值)?
  • 字符串变长还是变短? int 从 8 位变为 16 位有符号/无符号?
  • 也许我结合姓氏/名字,或将名字分成两个字段?

  • 这些只是一些简单的例子;我不是在寻找这些问题的答案,而是寻找一个通用的解决方案。

    显然,我需要一些硬编码逻辑来处理每个更改。

    如果有人没有从版本 x 升级到 x+1,而是等待 x+2 怎么办?我应该尝试组合这些更改,还是只应用 x -> x+ 1 后跟 x+1 -> x+2?

    如果版本 x+1 有问题并且我们需要回滚到 s/w 的先前版本,但已经“升级”了数据结构怎么办?

    我倾向于 TLV ( http://en.wikipedia.org/wiki/Type-length-value ) 但可以看到很多潜在的麻烦。

    这不是什么新鲜事,所以我只是想知道其他人是怎么做的......

    最佳答案

    我确实有一些代码,如果需要,可以将较长的字符串从两个较短的段拼凑在一起。哎呀。这是我保持某些数据兼容 12 年后的经验:

    定义您的目标 - 那里有两个:

  • 新版本应该能够读取旧版本写的内容
  • 旧版本应该能够阅读新版本写的内容(更难)

  • 添加版本支持到版本 0 - 至少写一个版本头。再加上保留(可能很多)旧的阅读器代码,可以原始地解决第一种情况。如果不想实现案例2,开始拒绝新数据现在 !

    如果您只需要案例 1,并且随着时间的推移预期的变化很小,那么您就可以了。无论如何,在第一次发布之前完成的这两件事可以为您省去很多麻烦。

    序列化期间转换 - 在运行时,只将数据以“新格式”保存在内存中。在持久性限制下进行必要的转换和测试(读取时转换为最新,写入时实现向后兼容)。这将版本问题隔离在一个地方,有助于避免难以追踪的错误。

    保留一组来自所有版本的测试数据。

    存储可用类型的子集 - 将实际序列化的数据限制为几种数据类型,例如int、string、double。在大多数情况下,额外的存储大小由支持这些类型更改的减少的代码大小组成。 (不过,这并不总是您可以在嵌入式系统上进行的权衡)。

    例如不要存储比 native 宽度短的整数。 (当您需要存储长整数数组时,您可能需要这样做)。

    添加断路器 - 存储一些允许您故意让旧代码显示此新数据不兼容的错误消息的 key 。您可以使用作为错误消息一部分的字符串 - 然后您的旧版本可能会显示一条它不知道的错误消息 - “您可以使用我们网站的 ConvertX 工具导入此数据”在本地化版本中不是很好应用程序,但仍然比“Ungültiges 格式”更好。

    不要直接序列化结构 - 这就是逻辑/物理分离。我们混合使用两种方法,各有利弊。这些都不能在没有运行时开销的情况下实现,这几乎会限制您在嵌入式环境中的选择。无论如何,在持久化期间不要使用固定的数组/字符串长度,这应该已经解决了一半的麻烦。

    (A) 合适的序列化机制 - 我们使用一个二进制序列化器,它允许在存储时启动一个“块”,它有自己的长度 header 。读取时,额外的数据被跳过,缺失的数据被默认初始化(这在序列化j代码中简化了很多“读取旧数据”的实现。)块可以嵌套。这就是您在物理方面所需要的全部内容,但需要为常见任务添加一些糖衣。

    (B) 使用不同的内存表示 - 内存中的再现基本上可以是 map<id, record>其中 id 可能是一个整数,而 record可能
  • 空(未存储)
  • 原始类型(字符串、整数、 double 型 - 使用越少越容易)
  • 原始类型数组
  • 和记录数组

  • 我最初是这样写的,所以这些人不会问我每个格式兼容性问题,虽然实现有很多缺点(我希望我能以今天的清晰度认识到这个问题......)它可以解决

    默认情况下,查询不存在的值将返回默认/零初始化值。当您在访问数据和添加新数据时牢记这一点时,这会很有帮助:想象一下,版本 1 会自动计算“foo 长度”,而在版本 2 中,用户可以覆盖该设置。零值 - 在“计算类型”或“长度”中应表示“自动计算”,并且您已设置。

    以下是您可以期待的“变化”场景:
  • 标志(是/否)扩展为枚举(“是/否/自动”)
  • 一个设置分为两个设置(例如,“添加边框”可以拆分为“偶数天添加边框”/“奇数天添加边框”。)
  • 添加一个设置,覆盖(或更糟的是,扩展)现有设置。

  • 对于实现案例 2,您还需要考虑:
  • 任何值都不能被删除或替换为另一个值。 (但在新格式中,可能会说“不支持”,并添加了一个新项目)
  • 枚举可能包含未知值,有效范围的其他更改

  • 呸。这是很多。但这并不像看起来那么复杂。

    关于c++ - 如何处理程序版本更新时不断变化的数据结构?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1665015/

    有关c++ - 如何处理程序版本更新时不断变化的数据结构?的更多相关文章

    1. ruby - 在 Ruby 程序执行时阻止 Windows 7 PC 进入休眠状态 - 2

      我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0

    2. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

      给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

    3. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

      我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

    4. ruby - 如何指定 Rack 处理程序 - 2

      Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

    5. ruby - 在 Ruby 中编写命令行实用程序 - 2

      我想用ruby​​编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序

    6. ruby-on-rails - Rails 应用程序之间的通信 - 2

      我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此

    7. ruby - 无法运行 Rails 2.x 应用程序 - 2

      我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby​​:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r

    8. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

      我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

    9. ruby-on-rails - Rails 应用程序中的 Rails : How are you using application_controller. rb 是新手吗? - 2

      刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr

    10. ruby-on-rails - 项目升级后 Pow 不会更改 ruby​​ 版本 - 2

      我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby​​版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby​​版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘

    随机推荐