At 06:03 AM 8/14/2003, Walter Landry wrote:

>Greetings,
>
>I've started using boost::filesystem recently, and I'm mostly very
>happy.

Wow! A very happy user. Or at least mostly very happy. That's good news:-) Seriously, it is a powerful motivator to get that kind of feedback.

>  One thing bothers me, though.  Why does it implement any
>restrictions, by default, on what kind of files it allows?

See below.

>...
>I also noticed that you can't open a directory named "." or "..",
>though I think "./" and "../" both work.

Hum... I'm not sure that is currently intended.

> Files starting or ending with spaces also don't work.

That is intended, although it may change if the portability checking scheme changes.

>
>I understand that I can (painfully) work around it by using a
>different context, but I don't understand why boost::filesystem wants
>to restrict me to a set of filenames that are portable.  Isn't that a
>bit too much handholding?  I don't mind having an is_portable()
>command or something similar, but it is incredibly annoying to have to
>abide by someone else's filename restrictions.

The current approach is clearly too restrictive and isn't satisfactory. Beyond the problems you mention, there really isn't a single standard for portability. Even 8.3 names aren't portable to systems which don't allow periods in names. A whole family of checkers is needed, giving various degrees of portability. Some should be supplied with the library, but users also need to be able to provide their own.

OTOH, a function that has to be explicitly called isn't going to be effective. Manual checking is too laborious in code that does a lot of path manipulation. A one time I took several pages of code and tried to add explicit checks. The code turned into a mess. Manual checking is also likely to be ignored by the very programmers who need it the most; those who have firm but erroneous ideas about what is or isn't portable.

So it comes down to an interface problem. What is the best way for the user specify a portability checking function to override the default checking function?

It would be trivial to add an additional path constructor that takes a checking function or function object.

But that would kill ease-of-use. It is one thing to require an additional constructor argument in the fairly limited use "native" case, but the portability checking is applied to each and every path object constructed, including the many path temporaries created by the automatic conversions from char * and string.

(That "kill ease-of-use" point might be hard to understand if you haven't actually written code using the library. The automatic conversions really do allow "script-like" programming ease, and are reasonably safe given the conversion result is class path.)

Also, the portability checking policy often needs to propagate to called functions such as third party liberties. Adding overloads of functions to take an additional argument is also too messy, and doesn't provide for automatic pass-through to lower level functions. In most (but not all) programs it really would be convenient if portability checking policy was a global. But of course we all know that "global variables are consider harmful". There are also several valid use cases where a program needs to switch back and forth between portability policies.

Another approach is to provide a way to tell class path that within a file scope use a policy other than the default. It would have to be carefully worked out to avoid ODR violations, and to ensure propagation to called library functions. I have not been able to come up with a workable version of this approach.

That was about as far as my thinking had gotten in the past. While composing this reply, I came up with another possible solution. Let's say <boost/filesystem/path.hpp> added this:

typedef bool (*is_portable_test)( string & candidate );

   class scoped_portability_policy : noncopyable
   {
   public:
     explicit scoped_portabiity_policy( is_portable_test f );
     ~scoped_portabiity_policy();
    };

The effect of constructing a scoped_portability_policy is to push p onto a stack. The destructor pops the stack. Class path uses the top stack entry as the current portability checker. The stack is initialized with a default portability checker.

Most programs which wanted a policy other than the default would just being something like this:

  int main()
  {
     scoped_portability_policy( posix_portability_check );
     ...

Although the stack is global, when coupled with scoped_portability_policy, it is safer than a single global variable (because library functions which push are guaranteed to pop.)

Of course it can be subverted by static or heap allocation. (Aside: Is there a reliable way to prevent static or heap allocation?)

I'm curious to hear what others would think of the above scheme.

--Beman


_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Reply via email to