Hot answers tagged

67

CI-driven development is fine! This is a lot better than not running tests and including broken code! However, there are a couple of things to make this easier on everyone involved: Set expectations: Have contribution documentation that explains that CI often finds additional issues, and that these will have to be fixed before a merge. Perhaps explain that ...


34

Building a sustainable plugin model requires that your core framework expose a stable interface that plugins can rely on. The golden rule is that you can introduce new interfaces over time but you can never modify an already published interface. If you follow this rule, you can refactor the implementation of the core framework all you want without fear of ...


8

so when they propose refactoring changes in the core (which these days happens on a quite regular basis), they checked that the code compiles on their machine before making a pull request on github. So I think this is where the loose style of open source projects can fall down; most centrally-organised projects are wary of core refactoring, especially when ...


8

To be honest, I don't think you can handle this in a better way - if changes result in breaking maintained parts of your project the CI should fail. Does your project have a contributing.md or something similar to help new and occasional contributors with preparing their contributions? Do you have a clear list, which plugins are part of the core and need ...


8

Actually, the bug in the Generator Control Units that you refer to is not the kind of memory handling bug that Rust (or any language with fixed-size integers) can protect against. An internal counter of the unit overflowed, which will happen eventually for any fixed-sized counter that keeps track of time for example, and the programmed safety features of the ...


5

Your incompatible Logger implementations cannot be linked because they have the same name. This leads to a simple solution that will solve all your problems: Incompatible versions should have different names. C++ makes following this approach comparatively easy because you can put those into separate namespaces, e.g. logging4::Logger vs logging5::Logger. ...


4

I also don't like the idea of having implementations of backend functions that just return errors. There is usually no one-size-fits-all solution for this. One has to decide on a by-case basis about the most appropriate solution. Here are some options: 1) Make an empty or "dummy" implementation of DoC, if you know for sure not reacting to a call of this ...


3

This is where encapsulation pays off. If you have functionality that's encapsulated in a method or class, that's written in the old MFC style, and you instantiate or call it using new-style code, it will still work, won't it? You don't ever have to look at that old code unless you need to maintain it. Treat your old code like the black box that it's ...


3

You assume that you will have one radio. Obviously you might have zero radios, and there is no reason to believe that there could never be multiple radios. To be future proof: Have a class that returns some kind of description for each available radio hardware. You'd probably return this information as an array. If there is no radio hardware, return an ...


2

I’d look into the Object Pool pattern that basically does the recycling you mention yourself. It’s actually how both Android and iOS handle the problem of “staggering” while scrolling on a list of items e.g. a newsfeed or product list. Close relatives are the Prototype and Flyweight patterns. Among these three and some sort of factory and depending on the ...


2

Things allocated using new are not automatically deleted. There needs to be a corresponding delete somewhere in your code. This can be very error-prone and is best avoided. If you must use new and delete, try to adopt an RAII approach - the new can only appear in a constructor, and the corresponding destructor always has the matching delete. If you want ...


2

When you're starting with C++, shun away from anything telling you to use new. You most probably do not need it. Memory management is very error prone, and has been solved by more modern approaches. Look up "value semantics" and RAII. And only when you really need dynamically allocated memory, find out what std::unique_ptr<double> can do for you. ...


2

Sounds like the CI process needs to be tighter, more comprehensive and more visible to contributors before they raise a PR. As an example, BitBucket have a pipelines feature that allows this, where you give it a file that defines in code the CI build process, and if it fails, the branch is prevented from being merged. Regardless of the technology, providing ...


2

There is no correct answer here. Your memcpy() code reinterprets bit patterns using the machine's endianess, and exercises undefined behaviour. For your bit vector, you can decide whether you enforce a specific endianess, or whether you stick the machine default. Neither approach is correct, this just depends on how you want to use the bit vector. In ...


2

The code works, they're happy, and then the CI finishes building and the problems start: compilation failed in a consortium-maintained plugin, that the contributor did not build on his/her machine. That plugin might have dependencies on third-party libraries, such as CUDA for instance, and the user does not want, does not know how to, or simply can't ...


1

The main purpose of standards and patterns is to improve the efficiency with which developers create and interact with code. They write code in a particular way, and expect to read code expressed in a particular way, and by doing this they reduce the amount of time spent evaluating how best to write code, and reduce the amount of time spent interpreting ...


1

You are implementing an array of bits. Bit positions are numbered from 0 upwards. There is no endianess involved at all. Now you have decide what bits you want to be set if you add say the number 4 as a 3 bit number. If your vector already contains 79 bits, then you add bits number 79, 80 and 81. It would make most sense if you add bit 0, 1 and 2 of the ...


1

No one else seems to have raised this as a potential solution. list all plugins that you can access. run all the tests that these plugins define record all request/responses/interactions between the core and all plugins store those recordings, these are now rough compatibility tests. When developing the core, encourage developers to run these compatibility ...


1

If contributing to the core without changing any contract can break dependent software, it suggests that either: The contracts of your interfaces may be ambiguous. Maybe adding attributes on your functions and function parameters would help in exposing additional constraints to client code to make the contracts clearer. Or if you’re applying contract-...


1

You need to separate inlining optimizations from the C++ inline keyword. Inlining optimizations are possible only if the target function is inline, or has internal linkage. And the call must not be virtual. This is independent from optimization levels because this is about fundamental language semantics. The inline keyword indicates that the function ...


1

As long as no heap memory is allocated, creating objects in C++ (when no calculations are performed in the constructor) isn't more expensive than calling a function and assigning individual variables, as can be seen in the following compilation result: https://godbolt.org/z/n016Fw If the constructor performs mathematical computations then it depends on the ...


1

Can memory handling errors be avoided by using only memory-safe languages? In brief, yes. They really do have the potential to eliminate a huge source of critical errors in the world's infrastructure. (There can still be memory handling errors in the implementation of the compilers and runtime systems for these languages, but those are usually written by far ...


1

It looks to me every other answer is missing the point. The point is that an interface should ideally define an atomic chunk of behavior. That is the I in SOLID. A class should have one responsibility but this could still include multiple behaviors. To stick with a typical database client object, this may offer full CRUD functionality. That would be four ...


Only top voted, non community-wiki answers of a minimum length are eligible