[yast-devel] New libstorage: Returning NULL pointers vs. throwing exceptions
When we put the new libstorage to the test in our tech previews, we had one typical situation occuring again and again: I get a pointer (or object reference in Ruby) to a base class object (typically BlkDevice), and I need to know what it actually is, or even just if it exists. Typical example: I need to check each filesystem on a disk for certain things. Since the disk might or might not have a partition table, first I need to check if it has one, and if it does, iterate over all partitions. If there is no partition table, there might be a filesystem directly on the disk (on, say, /dev/sdc rather than /dev/sdc1 - this might easily happen with USB sticks), so I need to check that, too. Now there are two approaches with an API like libstorage: (a) I query the disk for its partition table. If it doesn't have one, I get a NULL pointer (or nil object reference in Ruby). (b) I query the disk for its partition table. If it doesn't have one, an exception is thrown. At first thought, getting an exception for a quite common situation sounds like a bad idea. Worse, I might be tempted to introduce a new functionality: (c) Check if the disk has a partition table and only fetch that pointer/object reference if it does. If I fetch it when there is none, an exception is thrown. Basically, (c) is combining the worst of (a) and (b): I have to do a lot of checks and I might still get an exception. Worse, what happens behind the scenes when I call that has_partition_table() (C++) or partition_table? (Ruby) function is exactly the same as in (a): The lib fetches the pointer and checks if the object it points to has the desired type. In scenario (a), the corresponding Ruby code looks like this: # Check if a disk is our installation disk - the medium we just booted # and started the installation from. This will check all filesystems on # that disk. # def installation_disk?(disk) log.info("Checking if #{disk} is an installation disk") partition_table = disk.partition_table if partition_table partition_table.partitions.each do |partition| next if [::Storage::ID_SWAP, ::Storage::ID_EXTENDED].include?(partition.id) return true if installation_volume?(partition) end return false # No installation partition in the partition table end # Check if there is a filesystem directly on the disk # (without partition table). This is very common for installation # media such as USB sticks. installation_volume?(disk) end Here, just one check was necessary: 'if partition_table'. We got lucky here. If that expression was any longer, we might have to do a cascade of such tests: if disk && disk.partition_table && disk.partition_table.gpt? This gets annoying pretty quickly, and it doesn't exactly make the code better readable. Scenario (b) looks like this: # Check if a disk is our installation disk - the medium we just booted # and started the installation from. This will check all filesystems on # that disk. # def installation_disk?(disk) log.info("Checking if #{disk} is an installation disk") begin disk.partition_table.partitions.each do |partition| next if [::Storage::ID_SWAP, ::Storage::ID_EXTENDED].include?(partition.id) return true if installation_volume?(partition) end return false # if we get here, there is a partition table. rescue Exception => ex # No partition table on this disk log.info("CAUGHT exception: #{ex} for #{disk}") end # Check if there is a filesystem directly on the disk (without # partition table). This is very common for installation media such # as USB sticks. return installation_volume?(disk) end Notice that there is no check if the disk has a partition table - we just use it. Or, rather, we try to use it: That's why this is wrapped into that begin..rescue block. If there is no partition table, the lib will throw an exception, and code flow will continue in the 'rescue' clause. Coming from C++ and a world where exceptions are evil and should not occur within normal control flow, this sounds bad: We get an exception in a situation that might easily happen without anything being really broken. At least not broken in the sense that "uh-oh, we have a real problem here, we can't reasonable continue". OTOH if there would be more checks that might fail (such as the 'if disk && disk.partition_table && disk.partition_table.gpt?' check above), that one 'begin..rescue' block would handle them all, and the control flow remains clear and straightforward. And that's the beauty of it. Using exceptions and not checks all over the place can help to clarify the algorithm without clobbering the code with that checking code - code that is very rarely executed and thus mostly untested (try to create test cases for all those if/else branches!). So, even though right now libstorage writes each exception to the log and thus the log gets less pretty, we prefered to use the approach with exceptions rather than having to check each pointer/object reference. We also strongly believe in making life easier for the developers using the lib, even if that comes at the cost of making life harder for the lib developers. If we forced the lib users to check every piece of information they get from the lib because it might be NULL/nil, we'd bury land mines all over the place: Every pointer/object reference that might be NULL/nil is an accident waiting to happen. Even though uncaught exceptions are a similar class of errors, at least the exceptions carry enough information to track down the problem - and you get a backtrace that tells you what's going on at exactly the place where something goes wrong (unless a nil reference that might be used much later in the code). Kind regards -- Stefan Hundhammer <shundhammer@suse.de> YaST Developer SUSE Linux GmbH GF: Felix Imendörffer, Jane Smithard, Graham Norton; HRB 21284 (AG Nürnberg) Maxfeldstr. 5, 90409 Nürnberg, Germany -- To unsubscribe, e-mail: yast-devel+unsubscribe@opensuse.org To contact the owner, e-mail: yast-devel+owner@opensuse.org
On Tue, 24 Nov 2015 14:45:45 +0100 Stefan Hundhammer <shundhammer@suse.de> wrote:
When we put the new libstorage to the test in our tech previews, we had one typical situation occuring again and again: I get a pointer (or object reference in Ruby) to a base class object (typically BlkDevice), and I need to know what it actually is, or even just if it exists.
Typical example:
I need to check each filesystem on a disk for certain things. Since the disk might or might not have a partition table, first I need to check if it has one, and if it does, iterate over all partitions.
If there is no partition table, there might be a filesystem directly on the disk (on, say, /dev/sdc rather than /dev/sdc1 - this might easily happen with USB sticks), so I need to check that, too.
Now there are two approaches with an API like libstorage:
(a) I query the disk for its partition table. If it doesn't have one, I get a NULL pointer (or nil object reference in Ruby).
(b) I query the disk for its partition table. If it doesn't have one, an exception is thrown.
At first thought, getting an exception for a quite common situation sounds like a bad idea. Worse, I might be tempted to introduce a new functionality:
(c) Check if the disk has a partition table and only fetch that pointer/object reference if it does. If I fetch it when there is none, an exception is thrown.
Basically, (c) is combining the worst of (a) and (b): I have to do a lot of checks and I might still get an exception. Worse, what happens behind the scenes when I call that has_partition_table() (C++) or partition_table? (Ruby) function is exactly the same as in (a): The lib fetches the pointer and checks if the object it points to has the desired type.
Well, in OOP is also one more approach called NullObject pattern, which is create kind if partition table, that in represent no partition table. Let me below show how it will behave. ( and yes, I know it is a bit controversial pattern - https://en.wikipedia.org/wiki/Null_Object_pattern
In scenario (a), the corresponding Ruby code looks like this:
# Check if a disk is our installation disk - the medium we just booted # and started the installation from. This will check all filesystems on # that disk. # def installation_disk?(disk) log.info("Checking if #{disk} is an installation disk") partition_table = disk.partition_table if partition_table partition_table.partitions.each do |partition| next if [::Storage::ID_SWAP, ::Storage::ID_EXTENDED].include?(partition.id) return true if installation_volume?(partition) end return false # No installation partition in the partition table end
# Check if there is a filesystem directly on the disk # (without partition table). This is very common for installation # media such as USB sticks.
installation_volume?(disk) end
so if NoPartitionTable is used and its partitions return single partition, that is in fact filesystem on disk. def installation_disk?(disk) log.info("Checking if #{disk} is an installation disk") disk.partition_table.partitions.each do |partition| next if [::Storage::ID_SWAP, ::Storage::ID_EXTENDED].include?(partition.id) return true if installation_volume?(partition) end return false # No installation partition in the partition table end
Here, just one check was necessary: 'if partition_table'. We got lucky here. If that expression was any longer, we might have to do a cascade of such tests:
if disk && disk.partition_table && disk.partition_table.gpt?
This gets annoying pretty quickly, and it doesn't exactly make the code better readable.
JFYI new ruby 2.3 will have &. operator for such cases, so you can then write it like: if disk&.partition_table&.gpt? which looks a bit better, but still I prefer NullObject (for expected situations ) and exceptions ( for bugs or errors )
Scenario (b) looks like this:
# Check if a disk is our installation disk - the medium we just booted # and started the installation from. This will check all filesystems on # that disk. # def installation_disk?(disk) log.info("Checking if #{disk} is an installation disk") begin disk.partition_table.partitions.each do |partition| next if [::Storage::ID_SWAP, ::Storage::ID_EXTENDED].include?(partition.id) return true if installation_volume?(partition) end return false # if we get here, there is a partition table. rescue Exception => ex # No partition table on this disk log.info("CAUGHT exception: #{ex} for #{disk}") end
# Check if there is a filesystem directly on the disk (without # partition table). This is very common for installation media such # as USB sticks. return installation_volume?(disk) end
Notice that there is no check if the disk has a partition table - we just use it. Or, rather, we try to use it: That's why this is wrapped into that begin..rescue block. If there is no partition table, the lib will throw an exception, and code flow will continue in the 'rescue' clause.
Coming from C++ and a world where exceptions are evil and should not occur within normal control flow, this sounds bad: We get an exception in a situation that might easily happen without anything being really broken. At least not broken in the sense that "uh-oh, we have a real problem here, we can't reasonable continue".
It is very similar in ruby. Do not use exceptions for common situations.
OTOH if there would be more checks that might fail (such as the 'if disk && disk.partition_table && disk.partition_table.gpt?' check above), that one 'begin..rescue' block would handle them all, and the control flow remains clear and straightforward.
And that's the beauty of it.
Well, I am not sure as you should use more specialized exception catcher, otherwise you can catch also other problem, that should go more top level, so in the end, you might want to rescue from three kinds of exception like rescue NoPartitionTable, NoDisk, NotGpt => e which is not so beauty.
Using exceptions and not checks all over the place can help to clarify the algorithm without clobbering the code with that checking code - code that is very rarely executed and thus mostly untested (try to create test cases for all those if/else branches!).
on other hand testing all potential raiser of exception is also quite tricky.
So, even though right now libstorage writes each exception to the log and thus the log gets less pretty, we prefered to use the approach with exceptions rather than having to check each pointer/object reference.
We also strongly believe in making life easier for the developers using the lib, even if that comes at the cost of making life harder for the lib developers. If we forced the lib users to check every piece of information they get from the lib because it might be NULL/nil, we'd bury land mines all over the place: Every pointer/object reference that might be NULL/nil is an accident waiting to happen. Even though uncaught exceptions are a similar class of errors, at least the exceptions carry enough information to track down the problem - and you get a backtrace that tells you what's going on at exactly the place where something goes wrong (unless a nil reference that might be used much later in the code).
Still I think for common situation that can easily happen like disk without partitions we should consider NullPattern approach, which *for me* is in this situation better approach. Just my 2c. Josef
Kind regards
-- To unsubscribe, e-mail: yast-devel+unsubscribe@opensuse.org To contact the owner, e-mail: yast-devel+owner@opensuse.org
On 11/24/2015 03:15 PM, Josef Reidinger wrote:
On Tue, 24 Nov 2015 14:45:45 +0100 Stefan Hundhammer <shundhammer@suse.de> wrote:
[...]
Well, in OOP is also one more approach called NullObject pattern, which is create kind if partition table, that in represent no partition table. Let me below show how it will behave. ( and yes, I know it is a bit controversial pattern - https://en.wikipedia.org/wiki/Null_Object_pattern
Indeed, while reading HuHa's mail, the NullObject pattern was the first thing that came to my mind as well (it looks like a tailored example to explain the pattern). Fortunately, Josef already explained it much better that I would have done.
[...]
[...]
OTOH if there would be more checks that might fail (such as the 'if disk && disk.partition_table && disk.partition_table.gpt?' check above), that one 'begin..rescue' block would handle them all, and the control flow remains clear and straightforward.
And that's the beauty of it.
Well, I am not sure as you should use more specialized exception catcher, otherwise you can catch also other problem, that should go more top level, so in the end, you might want to rescue from three kinds of exception like
rescue NoPartitionTable, NoDisk, NotGpt => e
which is not so beauty.
Well, to be fair, you could use Inheritance like class LibStorage::NotFound < Exception; end class LibStorage::NoPartitionTable < LibStorage::NotFound; end class LibStorage::NoDisk < LibStorage::NotFound; end and so on, so you only need to catch rescue LibStorage::NotFound => e Still, I prefer the NullObject pattern instead of abusing the exceptions system (which, as a side note, used to be quite slow in the first versions of Ruby 2, not sure now).
[...]
Still I think for common situation that can easily happen like disk without partitions we should consider NullPattern approach, which *for me* is in this situation better approach.
+1 Cheers. -- Ancor González Sosa YaST Team at SUSE Linux GmbH -- To unsubscribe, e-mail: yast-devel+unsubscribe@opensuse.org To contact the owner, e-mail: yast-devel+owner@opensuse.org
On 24.11.2015 15:15, Josef Reidinger wrote:
Well, in OOP is also one more approach called NullObject pattern, which is create kind if partition table, that in represent no partition table.
This is wrong on so many levels that I don't even know where to begin... This proposed NullObject pretends to do a lot of things that it can't deliver. It makes false promises. And I can't even tell that it does that - to the outside world, it looks just like the real thing; the difference being that it does not do anything useful, so there is no reasonable way to handle the other case.
so if NoPartitionTable is used and its partitions return single partition, that is in fact filesystem on disk.
Only that I really need to know the difference in most cases because I have to do different things. To stay with the current example: A disk might have - an old-style MS-DOS type partition table with 4 slots (where I need to use an extended partition if I need any more partitions) - a new style GPT partition table that can have any number of parititions - a filesystem directly on the disk device without any partition table It sounds tempting to hide those gory details in the lib. But then, we do need to know a lot about this because we might have to do different things in the storage or bootloader proposal. It might just change the requirements if or when we need any of the boot partitions (/boot, EFI-boot, PReP) etc. etc. So, since we are doing deeply technical stuff here, I fear we need to know and to handle the gory details. Too much information hiding will become counterproductive here pretty quickly. There be dragons. ;-)
JFYI new ruby 2.3 will have &. operator for such cases, so you can then write it like:
if disk&.partition_table&.gpt?
Yikes, that's really ugly. When Moses came down mount Sinai, he must have dropped some stone slabs - that one, for example: Thou shalt not use crazy interpunction in the middle of source code. ;-)
which looks a bit better
I strongly disagree on that point.
OTOH if there would be more checks that might fail (such as the 'if disk && disk.partition_table && disk.partition_table.gpt?' check above), that one 'begin..rescue' block would handle them all, and the control flow remains clear and straightforward.
And that's the beauty of it.
Well, I am not sure as you should use more specialized exception catcher, otherwise you can catch also other problem, that should go more top level, so in the end, you might want to rescue from three kinds of exception like
rescue NoPartitionTable, NoDisk, NotGpt => e
Those things tend to accumulate - the exception types as well as the ones caught in the rescue clauses.
Using exceptions and not checks all over the place can help to clarify the algorithm without clobbering the code with that checking code - code that is very rarely executed and thus mostly untested (try to create test cases for all those if/else branches!).
on other hand testing all potential raiser of exception is also quite tricky.
There are much less cases to test. Kind regards -- Stefan Hundhammer <shundhammer@suse.de> YaST Developer SUSE Linux GmbH GF: Felix Imendörffer, Jane Smithard, Graham Norton; HRB 21284 (AG Nürnberg) Maxfeldstr. 5, 90409 Nürnberg, Germany -- To unsubscribe, e-mail: yast-devel+unsubscribe@opensuse.org To contact the owner, e-mail: yast-devel+owner@opensuse.org
On Tue, 24 Nov 2015 15:47:24 +0100 Stefan Hundhammer <shundhammer@suse.de> wrote:
On 24.11.2015 15:15, Josef Reidinger wrote:
Well, in OOP is also one more approach called NullObject pattern, which is create kind if partition table, that in represent no partition table.
This is wrong on so many levels that I don't even know where to begin...
I know it is a bit controversy, similar like Singleton pattern. I agree that over use it is bad design, but I also think it works nice in some situations.
This proposed NullObject pretends to do a lot of things that it can't deliver.
If something cannot be deliver then it is fair to raise exception in this method. It is common used approach. Where it make sense provide functionality and where it cannot deliver it, then raise exception.
It makes false promises.
promise depends on API contract.
And I can't even tell that it does that - to the outside world, it looks just like the real thing; the difference being that it does not do anything useful, so there is no reasonable way to handle the other case.
Comparing to `if not nil` or `catch Exception` I think it can do similar stuff like logging. Of course as I mention above that method can also raise exception if something is required that cannot be delivered.
so if NoPartitionTable is used and its partitions return single partition, that is in fact filesystem on disk.
Only that I really need to know the difference in most cases because I have to do different things. To stay with the current example: A disk might have
- an old-style MS-DOS type partition table with 4 slots (where I need to use an extended partition if I need any more partitions)
- a new style GPT partition table that can have any number of parititions
- a filesystem directly on the disk device without any partition table
So for me I can see it as three types of partition table - msdos, gpt and no. I do not see why is better to have begin if gpt? elsif msdos? end rescue # no partition table case end then simple if gpt? elsif msdos? elsif no? end
It sounds tempting to hide those gory details in the lib. But then, we do need to know a lot about this because we might have to do different things in the storage or bootloader proposal.
I do not see how how it hides details. It should be accessible for ones who is interested in it, but hide to ones who is not interested. Like in example above, if you need behave differently then detect it, but if you want find boot disk, you are not interested what kind of partition table is used.
It might just change the requirements if or when we need any of the boot partitions (/boot, EFI-boot, PReP) etc. etc.
So, since we are doing deeply technical stuff here, I fear we need to know and to handle the gory details. Too much information hiding will become counterproductive here pretty quickly.
For me it is not hiding, it just using specialized object or class as nil. Object that provide basic functionality or behavior for some cases and raise exception in others. Class that do not hide it is null one if you need to know it. so how can looks implementation for NoPartitionTable ( let me use ruby pseudocode to make it shorter ) class NoPartitionTable def create_partition(name, type) raise UnsupportedOperation, "Cannot create partition for partitionless disk" end def delete_partition(name) # act same as other classes when trying to delete non existing name end def get_partitions # return list with single element containing volume as partition end def get_partition(name) # act same as other classes when trying to get non existing name end def get_unused_partition_slot(all = true, logical = true) return [] end end and thats it. So no fake promises, no hidding details and it can be used in code interested in it. And advantage is also that usage is same as for other partition tables.
There be dragons. ;-)
JFYI new ruby 2.3 will have &. operator for such cases, so you can then write it like:
if disk&.partition_table&.gpt?
Yikes, that's really ugly.
I also more like try syntax from active support, but at least I see some logic in that syntax.
When Moses came down mount Sinai, he must have dropped some stone slabs - that one, for example:
Thou shalt not use crazy interpunction in the middle of source code.
;-)
which looks a bit better
I strongly disagree on that point.
probably matter of taste, I am too lazy to type too much characters :)
OTOH if there would be more checks that might fail (such as the 'if disk && disk.partition_table && disk.partition_table.gpt?' check above), that one 'begin..rescue' block would handle them all, and the control flow remains clear and straightforward.
And that's the beauty of it.
Well, I am not sure as you should use more specialized exception catcher, otherwise you can catch also other problem, that should go more top level, so in the end, you might want to rescue from three kinds of exception like
rescue NoPartitionTable, NoDisk, NotGpt => e
Those things tend to accumulate - the exception types as well as the ones caught in the rescue clauses.
It is questionable. If partitionless disks will be more common, then more code must cope with it and it makes more methods that have to be aware of it. So in the end it maybe not accumulate so much. That is reason why exception should be really exceptional case, which other code is usually not interested in.
Using exceptions and not checks all over the place can help to clarify the algorithm without clobbering the code with that checking code - code that is very rarely executed and thus mostly untested (try to create test cases for all those if/else branches!).
on other hand testing all potential raiser of exception is also quite tricky.
There are much less cases to test.
I worry that in more complex or top level code it can end that any line can raise multiple exceptions, which can make tests hard, but I do not have experience with using exceptions for this purpose.
Kind regards
Josef -- To unsubscribe, e-mail: yast-devel+unsubscribe@opensuse.org To contact the owner, e-mail: yast-devel+owner@opensuse.org
Dne 24.11.2015 v 16:45 Josef Reidinger napsal(a):
On Tue, 24 Nov 2015 15:47:24 +0100 Stefan Hundhammer <shundhammer@suse.de> wrote: [...]
I have to do different things. To stay with the current example: A disk might have
- an old-style MS-DOS type partition table with 4 slots (where I need to use an extended partition if I need any more partitions)
- a new style GPT partition table that can have any number of parititions
- a filesystem directly on the disk device without any partition table
So for me I can see it as three types of partition table - msdos, gpt and no. I do not see why is better to have
begin if gpt? elsif msdos? end rescue # no partition table case end
If no partition is a valid use case (and it is as mentioned above) then it does not make sense to raise exception in that case. Exceptions should be used only in error cases to handle *unexpected* things...
then simple
if gpt? elsif msdos? elsif no? end
+1, this looks better to me... -- Ladislav Slezák Appliance department / YaST Developer Lihovarská 1060/12 190 00 Prague 9 / Czech Republic tel: +420 284 028 960 lslezak@suse.com SUSE -- To unsubscribe, e-mail: yast-devel+unsubscribe@opensuse.org To contact the owner, e-mail: yast-devel+owner@opensuse.org
On Tue, Dec 01, 2015 at 09:21:26AM +0100, Ladislav Slezak wrote:
Dne 24.11.2015 v 16:45 Josef Reidinger napsal(a):
On Tue, 24 Nov 2015 15:47:24 +0100 Stefan Hundhammer <shundhammer@suse.de> wrote: [...]
I have to do different things. To stay with the current example: A disk might have
- an old-style MS-DOS type partition table with 4 slots (where I need to use an extended partition if I need any more partitions)
- a new style GPT partition table that can have any number of parititions
- a filesystem directly on the disk device without any partition table
So for me I can see it as three types of partition table - msdos, gpt and no. I do not see why is better to have
begin if gpt? elsif msdos? end rescue # no partition table case end
If no partition is a valid use case (and it is as mentioned above) then it does not make sense to raise exception in that case. Exceptions should be used only in error cases to handle *unexpected* things...
No, to quote Stroustrup: "exceptions are used to signal errors that cannot be handled locally". Casting to a wrong type or requesting a non-existing object is an error that cannot be handled locally and thus must be signaled, e.g. by an exception. Regards, Arvin -- Arvin Schnell, <aschnell@suse.com> Senior Software Engineer, Research & Development SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg) Maxfeldstraße 5 90409 Nürnberg Germany -- To unsubscribe, e-mail: yast-devel+unsubscribe@opensuse.org To contact the owner, e-mail: yast-devel+owner@opensuse.org
On Tue, 1 Dec 2015 13:46:12 +0100 Arvin Schnell <aschnell@suse.com> wrote:
On Tue, Dec 01, 2015 at 09:21:26AM +0100, Ladislav Slezak wrote:
Dne 24.11.2015 v 16:45 Josef Reidinger napsal(a):
On Tue, 24 Nov 2015 15:47:24 +0100 Stefan Hundhammer <shundhammer@suse.de> wrote: [...]
I have to do different things. To stay with the current example: A disk might have
- an old-style MS-DOS type partition table with 4 slots (where I need to use an extended partition if I need any more partitions)
- a new style GPT partition table that can have any number of parititions
- a filesystem directly on the disk device without any partition table
So for me I can see it as three types of partition table - msdos, gpt and no. I do not see why is better to have
begin if gpt? elsif msdos? end rescue # no partition table case end
If no partition is a valid use case (and it is as mentioned above) then it does not make sense to raise exception in that case. Exceptions should be used only in error cases to handle *unexpected* things...
No, to quote Stroustrup: "exceptions are used to signal errors that cannot be handled locally". Casting to a wrong type or requesting a non-existing object is an error that cannot be handled locally and thus must be signaled, e.g. by an exception.
I fully agree with stroustrup that if you have error that cannot be handled locally raise exception. Still I do not see how having disk without partition table can be error if it is supported and expected use case. Error for me is if there is partition table, but we cannot read it, it is corrupted, etc. but not in expected scenario. Josef
Regards, Arvin
-- To unsubscribe, e-mail: yast-devel+unsubscribe@opensuse.org To contact the owner, e-mail: yast-devel+owner@opensuse.org
On Tue, Dec 01, 2015 at 02:09:21PM +0100, Josef Reidinger wrote:
On Tue, 1 Dec 2015 13:46:12 +0100 Arvin Schnell <aschnell@suse.com> wrote:
No, to quote Stroustrup: "exceptions are used to signal errors that cannot be handled locally". Casting to a wrong type or requesting a non-existing object is an error that cannot be handled locally and thus must be signaled, e.g. by an exception.
I fully agree with stroustrup that if you have error that cannot be handled locally raise exception. Still I do not see how having disk without partition table can be error if it is supported and expected use case. Error for me is if there is partition table, but we cannot read it, it is corrupted, etc. but not in expected scenario.
Having a string with 1000 characters is supported. Still, if you have a string with only 100 characters and you request the 200st character you will cause an exception. It's the same with the device graph in libstorage. A disk can have a partition table or a filesystem (or something else) as a child. Requesting the filesystem if the child is not a filesystem raises an exception (as explained above). But this discussion is once again unfruitful. In programming it is not black and white. Those that implement something have to find one way, others would have found other ways. Regards, Arvin -- Arvin Schnell, <aschnell@suse.com> Senior Software Engineer, Research & Development SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg) Maxfeldstraße 5 90409 Nürnberg Germany -- To unsubscribe, e-mail: yast-devel+unsubscribe@opensuse.org To contact the owner, e-mail: yast-devel+owner@opensuse.org
Dne 24.11.2015 v 16:45 Josef Reidinger napsal(a):
On Tue, 24 Nov 2015 15:47:24 +0100 Stefan Hundhammer <shundhammer@suse.de> wrote:
On 24.11.2015 15:15, Josef Reidinger wrote:
Well, in OOP is also one more approach called NullObject pattern, which is create kind if partition table, that in represent no partition table.
This is wrong on so many levels that I don't even know where to begin...
I know it is a bit controversy, similar like Singleton pattern. I agree that over use it is bad design, but I also think it works nice in some situations.
Null objects are used in libzypp quite a lot (e.g. [1], [2]). The libzypp API is huge and IMHO well designed. Sometimes too many templates or "magic" boost patterns, but that's probably because I'm not an experienced C++ developer. In general I like the API. Maybe we could ask the libzypp developers for their experience with such a huge API? To get some recommendations and early feedback... [1] https://github.com/openSUSE/libzypp/blob/master/zypp/Repository.h#L62 [2] https://github.com/openSUSE/libzypp/blob/master/zypp/RepoInfo.h#L80 -- Ladislav Slezák Appliance department / YaST Developer Lihovarská 1060/12 190 00 Prague 9 / Czech Republic tel: +420 284 028 960 lslezak@suse.com SUSE -- To unsubscribe, e-mail: yast-devel+unsubscribe@opensuse.org To contact the owner, e-mail: yast-devel+owner@opensuse.org
On Tue, Dec 01, 2015 at 09:42:38AM +0100, Ladislav Slezak wrote:
Dne 24.11.2015 v 16:45 Josef Reidinger napsal(a):
On Tue, 24 Nov 2015 15:47:24 +0100 Stefan Hundhammer <shundhammer@suse.de> wrote:
On 24.11.2015 15:15, Josef Reidinger wrote:
Well, in OOP is also one more approach called NullObject pattern, which is create kind if partition table, that in represent no partition table.
This is wrong on so many levels that I don't even know where to begin...
I know it is a bit controversy, similar like Singleton pattern. I agree that over use it is bad design, but I also think it works nice in some situations.
Null objects are used in libzypp quite a lot (e.g. [1], [2]).
But they are not used everywhere. Look for exceptions in y2log.
The libzypp API is huge and IMHO well designed. Sometimes too many templates or "magic" boost patterns, but that's probably because I'm not an experienced C++ developer. In general I like the API.
And that is why we have to manually maintain bindings? Those bindings deal with exceptions as well and "catch (...) {}" is commonly used. My approach is KISS.
Maybe we could ask the libzypp developers for their experience with such a huge API? To get some recommendations and early feedback...
I have already done so in the past and will continue to do so. Regards, Arvin -- Arvin Schnell, <aschnell@suse.com> Senior Software Engineer, Research & Development SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg) Maxfeldstraße 5 90409 Nürnberg Germany -- To unsubscribe, e-mail: yast-devel+unsubscribe@opensuse.org To contact the owner, e-mail: yast-devel+owner@opensuse.org
On Tue, 1 Dec 2015 13:55:50 +0100 Arvin Schnell <aschnell@suse.com> wrote:
On Tue, Dec 01, 2015 at 09:42:38AM +0100, Ladislav Slezak wrote:
Dne 24.11.2015 v 16:45 Josef Reidinger napsal(a):
On Tue, 24 Nov 2015 15:47:24 +0100 Stefan Hundhammer <shundhammer@suse.de> wrote:
On 24.11.2015 15:15, Josef Reidinger wrote:
Well, in OOP is also one more approach called NullObject pattern, which is create kind if partition table, that in represent no partition table.
This is wrong on so many levels that I don't even know where to begin...
I know it is a bit controversy, similar like Singleton pattern. I agree that over use it is bad design, but I also think it works nice in some situations.
Null objects are used in libzypp quite a lot (e.g. [1], [2]).
But they are not used everywhere. Look for exceptions in y2log.
Yeah, both approach is bad, overusing it for everything and also do not use it where it fits.
The libzypp API is huge and IMHO well designed. Sometimes too many templates or "magic" boost patterns, but that's probably because I'm not an experienced C++ developer. In general I like the API.
And that is why we have to manually maintain bindings? Those bindings deal with exceptions as well and "catch (...) {}" is commonly used.
Actaully reason for bindings is that e.g. swig cannot generate ycp bindings ale all catches are result of our component system limitation, not by libzypp limitations.
My approach is KISS.
question is if it is KISS for library users or KISS of implementation.
Maybe we could ask the libzypp developers for their experience with such a huge API? To get some recommendations and early feedback...
I have already done so in the past and will continue to do so.
Good, is it somewhere documented, e.g. some discussion and more important results of such discussion? It can help in future understand better why something is done in chosen way. Josef
Regards, Arvin
-- To unsubscribe, e-mail: yast-devel+unsubscribe@opensuse.org To contact the owner, e-mail: yast-devel+owner@opensuse.org
On Tue, Dec 01, 2015 at 02:13:57PM +0100, Josef Reidinger wrote:
Maybe we could ask the libzypp developers for their experience with such a huge API? To get some recommendations and early feedback...
I have already done so in the past and will continue to do so.
Good, is it somewhere documented, e.g. some discussion and more important results of such discussion? It can help in future understand better why something is done in chosen way.
Some decisions are documented. Regards, Arvin -- Arvin Schnell, <aschnell@suse.com> Senior Software Engineer, Research & Development SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg) Maxfeldstraße 5 90409 Nürnberg Germany -- To unsubscribe, e-mail: yast-devel+unsubscribe@opensuse.org To contact the owner, e-mail: yast-devel+owner@opensuse.org
On Tue, Nov 24, 2015 at 03:15:57PM +0100, Josef Reidinger wrote:
Well, in OOP is also one more approach called NullObject pattern, which is create kind if partition table, that in represent no partition table. Let me below show how it will behave. ( and yes, I know it is a bit controversial pattern - https://en.wikipedia.org/wiki/Null_Object_pattern
so if NoPartitionTable is used and its partitions return single partition, that is in fact filesystem on disk.
And when you ask that single "fake" partition for it's device name, will it be "/dev/sda" or "/dev/sda1"? Any answer is looking for trouble, e.g. with "/dev/sda" you have several objects with the same device name (have fun finding the right one), with "/dev/sda1" you cannot e.g. use the device name to mount the filesystem. Regards, Arvin -- Arvin Schnell, <aschnell@suse.com> Senior Software Engineer, Research & Development SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg) Maxfeldstraße 5 90409 Nürnberg Germany -- To unsubscribe, e-mail: yast-devel+unsubscribe@opensuse.org To contact the owner, e-mail: yast-devel+owner@opensuse.org
On Tue, 24 Nov 2015 15:59:50 +0100 Arvin Schnell <aschnell@suse.com> wrote:
On Tue, Nov 24, 2015 at 03:15:57PM +0100, Josef Reidinger wrote:
Well, in OOP is also one more approach called NullObject pattern, which is create kind if partition table, that in represent no partition table. Let me below show how it will behave. ( and yes, I know it is a bit controversial pattern - https://en.wikipedia.org/wiki/Null_Object_pattern
so if NoPartitionTable is used and its partitions return single partition, that is in fact filesystem on disk.
And when you ask that single "fake" partition for it's device name, will it be "/dev/sda" or "/dev/sda1"? Any answer is looking for trouble, e.g. with "/dev/sda" you have several objects with the same device name (have fun finding the right one), with "/dev/sda1" you cannot e.g. use the device name to mount the filesystem.
Well, if idea is that disk act as partition, then for me naive answer is /dev/sda as /dev/sda is disk and also partition with file system. So is expected that name is unique identifier of object? Maybe it can be object, that is both as it is for me idea of disk without partitions, that it itself act as partition. Josef
Regards, Arvin
-- To unsubscribe, e-mail: yast-devel+unsubscribe@opensuse.org To contact the owner, e-mail: yast-devel+owner@opensuse.org
On Tue, Nov 24, 2015 at 04:52:01PM +0100, Josef Reidinger wrote:
On Tue, 24 Nov 2015 15:59:50 +0100 Arvin Schnell <aschnell@suse.com> wrote:
On Tue, Nov 24, 2015 at 03:15:57PM +0100, Josef Reidinger wrote:
Well, in OOP is also one more approach called NullObject pattern, which is create kind if partition table, that in represent no partition table. Let me below show how it will behave. ( and yes, I know it is a bit controversial pattern - https://en.wikipedia.org/wiki/Null_Object_pattern
so if NoPartitionTable is used and its partitions return single partition, that is in fact filesystem on disk.
And when you ask that single "fake" partition for it's device name, will it be "/dev/sda" or "/dev/sda1"? Any answer is looking for trouble, e.g. with "/dev/sda" you have several objects with the same device name (have fun finding the right one), with "/dev/sda1" you cannot e.g. use the device name to mount the filesystem.
Well, if idea is that disk act as partition, then for me naive answer is /dev/sda as /dev/sda is disk and also partition with file system. So is expected that name is unique identifier of object?
Sure, for block devices the device name is a unique identifier. Regards, Arvin -- Arvin Schnell, <aschnell@suse.com> Senior Software Engineer, Research & Development SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg) Maxfeldstraße 5 90409 Nürnberg Germany -- To unsubscribe, e-mail: yast-devel+unsubscribe@opensuse.org To contact the owner, e-mail: yast-devel+owner@opensuse.org
participants (5)
-
Ancor Gonzalez Sosa
-
Arvin Schnell
-
Josef Reidinger
-
Ladislav Slezak
-
Stefan Hundhammer