Coder Social home page Coder Social logo

cpptypedids's Introduction

What is it for ?

In some code bases we need to refer to entities by their ids (an id beeing a more or less opaque 32bit, 64bit or 128bit chunk of data). For example, we have some kind of relational schema and we maintain relashionships as position in an array of entities or anything close.

The problem with such code base, is that it is easy to compare stuff that should not be compared. For example in complex queries to such a schema, we could compare ids of incompatible types. Exemple:

std::vector<Contract> findConstractByContractorAndRequiredSkill(std::uint32_t contractorId, std::uint32_t skillId){
  // std::map<std::uint32_t, std::set<uint32_t>> _contractsByContractorIndex;
  // std::map<std::uint32_t, std::set<uint32_t>> _contractsByRequiredSkillIndex;
  auto& filteredByContractor = _contractsByContractorIndex[contractorId];
  auto& filteredByRequiredSkill = _contractsByRequiredSkillIndex[contractorId];
  std::vector<std::uint32_t> contractIds;
  std::set_intersection(filteredByContractor.begin(), filteredByContractor.end(), filteredByRequiredSkill.begin(), filteredByRequiredSkill.end(), std::back_inserter(contractIds));
  // ... //
}

There is a typo error in this code that is not catched at compile time : we are using the contractorId instead of the skillId parameter to look into the required skill index. The same kind of error could happen, if I had a typo error and was looking in an incorrect index returning ids to something that is not a contract id.

This project helps to recover from such errors.

#How does it work ? In order to protect ourselves from such trouble, we need a stronger type for our ids. So what I want to obtain is somthing that lets me write things like that:

struct MyId : public [something]{}

and have MyId wrapping a std::uint32_t, and beeing assignable and comparable only to an object of type MyId.

But at the same time, from a performance perspective, we would like to get the same performance profile as with std::uint32_t as ids.

Fortunately, thanks to template meta-programming and inlining, it is completely achievable, and using this library, you can easily fix the broken code sample above:

using namespace simonstuff;
struct ContractorId : public typed_id_32<ContractorId>{  };
struct SkillId : public typed_id_32<SkillId>{  };
struct ContractId : public typed_id_32<ContractId>{  };

std::vector<Contract> findConstractByContractorAndRequiredSkill(ContractorId contractorId, SkillId skillId){
  // std::map<ContractorId, std::set<ContractId>> _contractsByContractorIndex;
  // std::map<SkillId, std::set<ContractId>> _contractsByRequiredSkillIndex;
  auto& filteredByContractor = _contractsByContractorIndex[contractorId];
  auto& filteredByRequiredSkill = _contractsByRequiredSkillIndex[contractorId]; // build error, type mismatch
  std::vector<ContractId> contractIds;
  std::set_intersection(filteredByContractor.begin(), filteredByContractor.end(), filteredByRequiredSkill.begin(), filteredByRequiredSkill.end(), std::back_inserter(contractIds));
  // ... //
}

So there we have type safety (different kinds of ids are not assignable to one another, and do not support comparison operators).

But how do we achieve our performance goal ?

First we have to look of what typed_id_32 is. It is an alias to typed_id<TID, std::uint32_t>. typed_id is only a wrapper around its underlying type (this is its only member variable), and it does not declare a virtual destructor (so make sure you don't declare any member variable in your inheriting types). So in memory it has the exact same size and aligment requirement as its underlying type. Then, the comparison functions are declared in the .h : thus they are inlined. As all they do is calling the comparison operator of the underlying type and accessing the only member variable of the type, compilers are able to remove all this type safety net at compile time, thus giving the same performance as if we had only std::uint32_t typed ids. This has been prooved by micro-benchmarking (not included in the github code, but within a production project elsewhere).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.