-
Notifications
You must be signed in to change notification settings - Fork 762
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
obs_api revision and improvements: new namespace etc. #530
obs_api revision and improvements: new namespace etc. #530
Conversation
src/pj_obs_api.c
Outdated
void pj_context_free (const PJ *P) { | ||
/* Create a new context - or provide a pointer to the default context */ | ||
/* Prepared for more elaborate sscanf style setup syntax */ | ||
PJ_CONTEXT *proj_context_create (char *setup, ...) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should be const char*
src/pj_obs_api.c
Outdated
va_list args; | ||
|
||
/* The classic single threaded, backwards compatible way */ | ||
if (0==setup) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hum, sounds a bit weird that a proj_context_create() function returns a default non-thread-safe context , but if this is documented...
src/pj_obs_api.c
Outdated
return ctx; | ||
|
||
/* Multi threaded with thread specific logging */ | ||
va_start (args, setup); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
va_list are notoriously hard to use correctly. Just ask users and developers of libtiff TIFFGetField() / TIFFSetField()... Why not having setters on the PJ_CONTEXT* ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have never seen anyone having problems with sscanf/sprintf, and it is that mode of operation that was intended here.
We could have setters/getters, but if/as PJ_CONTEXT grows (se comment below) that would also mean some risk of API-sprawl.
Also, I would much prefer letting the context be a mostly immutable thing: If you need it to mutate, you should create a new one, and suffer the consequences. This is, however, not a firm pronciple of mine, just a matter of seeing the context as a stable scene on which the PJs do their live performance :-)
* After a year of experiments (and previous experience from the | ||
* trlib transformation library) it has become clear that the | ||
* context subsystem is unavoidable in a multi-threaded world. | ||
* Hence, instead of hiding it away, we move it into the limelight, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I must confess I'm not convinced at all this needs to be exposed, and this clutters the API, so we should really have strong reasons to expose it. Why wouldn't we have a hidden context per PJ* object. What are the use cases of sharing contexts among PJ* objects ?
I believe that even in the "old" API the concept of context could / should have stayed remained as an implementation detail of the projPJ structure
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rouault - The context provides the pj_errno that makes it possible to signal what went wrong in a non-succesful call to proj_create, where we (as in the classic pj_init) return 0 for "non-succesfull).
Yes, it would be nice to just set an errno on the returned object, but since we use a signalling null-pointer, to actually report what went wrong, we need an errno style variable, and we need it to be thread-local in the multithread case.
Hence my suggestion that we use errno, which is thread local on multithreaded systems, and which is intended to signal library call errors. But you argued against both this and explicitly thread local storage class pj_errno a few days ago - which is the reason I had to switch to explicit context handling.
You can't have it both ways - it's either signalling through errno (or a thread local storage class pj_errno) OR explicit contexts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not a PJ * proj_create(PJ_CONTEXT *ctx, const char *definition, int *pnErrno) then ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First, we could leave out the context in that case.
Second it does not really seem C-idiomatic (closest thing is strtod (const char* str, char** endptr)
, but to my C-eyes you set errno when you return a null-pointer, you set errno to signal what went wrong. Looking at the use of errno in pj_init, I actually also believe this was the original design, although setting errno to 0 inside a library should be a no-no: You can use it to signal an error, but should not stomp on the signal from a neighbouring function or library, if you do not have anything to tell.
Third, I believe we can have much potential use (experience from trlib) in explicit contexts, that e.g. can be used to handle grid catalogs on behalf of a number of PJs, or for debugging assistance through reference counting (yell if you close a context before all PJs on that context are destroyed) - and in general, the contexts give us another degree of freedom for working around less fortunate design decisions of former times.
But really, I would MUCH prefer to skip contexts totally and use (the inherently thread local) errno for messaging. However, in case that is not possible, we might as well get as much utility out of this as possible
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
errno can work of course, and I'd be fine if you decide this it the way to go. It is just I'm personnally always a bit unconfortable with it as you must take care of saving it before calling something else that might modify it behind your back.
Regarding contexts, if I sum up the situation, basically the choice is between "we don't need exposing explicit context today, so don't expose them" and "we don't need exposing explicit context today, but might at some later point, so expose them". I'll defer to your decision.
Regarding grid loading, we probably want grids to be a global resource protected by a global mutex, as done currently, so as to avoid memory usage to grow too much. Although I remember people complaining using proj in a multithreaded scenario, where one thread requiring grid loading would block other threads not needing grid. I might have fixed this in the past, or at least looked at that issue :-) Can't remember. So yes perhaps in this scenario having an explicit context option "load grids in this context, and not in the global context, so as to avoid being blocked by other threads" could be beneficial
src/proj.h
Outdated
void pj_free (PJ *P); | ||
PJ *proj_create (PJ_CONTEXT *ctx, const char *definition); | ||
PJ *proj_create_argv (PJ_CONTEXT *ctx, int argc, char **argv); | ||
PJ *proj_create_pipeline (PJ_CONTEXT *ctx, const char *def_left, const char *def_right); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought pipelines where multi-stages transform. left and right seems to be a limitation to 2
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer def_from
and def_to
instead. The idea is to have a function that let's a user create a pipeline that converts from one CRS to another CRS. Example:
PJ pipe = proj_create_pipeline(ctx, "+init:epsg:25832", "+init=epsg:25833");
which can then be used by the transformation functions. For now it will be implemented as a simple pipeline that uses WGS84 as a hub (early binding, and similar to the existing API), but eventually it can be expanded to use a more complicated setup based on the EPSG-database that makes the direct transformation (late binding).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestions for a more descriptive name: proj_crs_to_crs
, proj_create_crs_to_crs
or proj_crs_to_crs_pipeline
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pipelines are multi stage. This is a pipelined equivalent to the pj_transform case of utilizing a pivot datum, but with the added potential for searching for a real solution elsewhere (e.g. in the epsg file). Ordinary pipelines are instantiated through ordinary pj_create calls
Huh, this doesn't match my experience. Before "recent" gcc versions, that now warn if the format string is inconsistent with the parameter list, this was a common problem to get it wrong and only realize at runtime. va_list use works, but requires really special care as the compiler cannot check anything for you. For example, you could pass a function that wouldn't have the expected prototype.
Note: a context should only be used by one thread at a time, so you normally shouldn't modify it whereas it is used by another proj_ function. Not exposing setters indeed avoids that potential issue. But you shouldn't also use different PJ* objects creating with the same context from different threads. |
@rouault - That's exactly my point: Contexts are thread specific, errno is thread local. If we use contexts for nothing but pj_errno, we might as well use errno and get rid of contexts altogether. But now that we seem to be stuck with contexts, we might as well prepare to get additional utility from them. |
… fwd/invcoord to PJ
…e more with added parens
…, mostly in test code, to reflect API changes
src/pj_obs_api.c
Outdated
/* The slightly convolved incremental indexing is used due */ | ||
/* to the stride, which may be any size supported by the platform */ | ||
for (i = 0; i < nmin; i++) { | ||
coord.xyzt.x = *x; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will only work on Intel and other architectures that accept unaligned access. For something more robust accross architectures, use memcpy(&(coord.xyzt.x, x, sizeof(double))
Or should we mention in the doc that despite the stride being in bytes, users should make sure that &x[stride*some_value] is propertly aligned ? In which case it would be safer to revert to the stride being specified in number of doubles
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The alignment will be OK because sizeof(whatever) includes the necessary alignment slack on platforms that need it - otherwise foo *p = calloc (300, sizeof(foo));
would not work: p[0].bar
is situated sizeof(foo)
bytes from p[1].bar
, the same is the case for p[0].baz
and p[1].baz
.
What we cannot assume is that sizeof(foo)==sum_of_the_size_of_its_elements
, since there may be slack both between the struct elements, and at the end of the struct (but not at the beginning: The first element of a struct is required to live at the same address as the struct itself).
This is the reason for my comment about "any size supported by the platform", i.e. any size you can get from the sizeof-operator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK OK, so that confirms we require that x, y, z, t array addresses and the stride provided are properly setup for dereferencing double values. And that we don't pretend to support unaligned accesses, which is reasonable. Could be worth mentionning in the function doc so that people don't have false expectations ( someone could devise a struct { double* y; double* x; char c[1]; } attribute ((packed)) and expect proj to properly work on that. I admit this is convoluted )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, certainly convoluted, but strange things happen in the wild, so I will try to put emphasis on this in the docs....
|
||
/* in all full length cases, we overwrite the input with the output */ | ||
if (nx > 1) { | ||
*x = coord.xyzt.x; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here: memcpy(x, &(coord.xyzt.x), sizeof(double)) (and for the below *y, *z, *t)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... and same reply :-)
src/pj_obs_api.c
Outdated
/* ... or would we rather not? Then what about the nmin==1 case? */ | ||
/* perhaps signalling the non-array case by setting stride to 0? */ | ||
if (nx==1) | ||
*x = coord.xyzt.x; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
memcpy(...)
src/pj_obs_api.c
Outdated
3. of length one, i.e. a constant, which will be treated as a fully | ||
populated array of that constant value | ||
|
||
The stride represents the step length, in bytes, between consecutive elements |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we want to be super general, perhaps the stride should be a stridex, stridey, stridez, stridet ? Sometimes for performance reasons you pack together x,y in the same structure, and have a dedicated array for z.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds like a good idea, although it will stretch an already long parameter list a bit more. I'm sure you have seen many more real world use cases than I have, so I will look into it.
Would this be useful for use cases in GDAL, or is it more a general comment? For example, I agree that in coming years, we probably will see quite a few cases of "grafting elevation support onto legacy code", by adding a separate elevation array to an established 2D code base :-)
src/pj_obs_api.c
Outdated
This is similar to the inner workings of the pj_transform function, but the | ||
idea of the stride has been generalized to work for any size of basic unit, | ||
not just a number of doubles. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Document the meaning of the return value ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure - there is a lot of proper documentation writing ahead - right now I'm just trying to get the most important things in as comments. I will add the thing about returning the number of coordinates actually transformed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pleased to see this array based transformer
will expose it to macro expansion, to the tune of much confusion and agony. | ||
|
||
**************************************************************************************/ | ||
int last_errno; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not really enthousiastic about this new error mechanism being added... Furthermore, apparently, it isn't used in the code. And shouldn't that declaration be extern int last_errono to avoid it being redefined in all compilation units that include projects.h ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it should not be extern int
: It is a part of the PJ
object, so extern
ing it would be a syntax error.
The PJ
local last_error is used in proj_errno_set()
, and since I'm trying to introduce some slightly more sane, but backwards compatible and not very intrusive, error reporting mechanism for the OBS API. I think adding an int
(i.e. typically 4 or 8 bytes) to the already rather large PJ
object is a small price to pay.
I see now that proj_errno
lacks a check for the PJ local last_error
, which I will add in a moment - the point is that it should return the local if nonzero, otherwise go on and search for the context specific, and (perhaps) finally the default context specific, because not all corners of the code can be supposed to support the PJ local reporting at once.
Note that the PJ local errno bubbles up to the context level (se comment in code), for compatibility with proj_api.h
and projects.h
based code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah sorry I didn't see with the diff format that this was a new field in a structure... So disregard this part of my comment.
src/proj.h
Outdated
@@ -328,6 +340,17 @@ extern const PJ *proj_shutdown; | |||
#endif | |||
|
|||
|
|||
/* Set or read error level */ | |||
int proj_errno (PJ *P); | |||
void proj_errno_set (PJ *P, int err); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don''t see the point in having proj_errno_set() and proj_context_errno_set() in the public API. They should be in a proj_private.h
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They are quite useful for reporting from application specific functions, that take PJ
arguments: In the main program one just need to do one kind of error checking, not having to care whether an error was reported from the code in function_doing_my_proj_stuff(PJ *P, ...)
or in the API code called by that function.
pj_ctx_set_errno()
is also part of proj_api.h
- I assume for the same reason.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"""They are quite useful for reporting from application specific functions, that take PJ arguments.""" Hum, given that proj errno numbers are an internal detail, I don't see how user applications could know which code to use.
I'm just fighting to keep proj.h at the very strict minimum to reduce the API surface.
"pj_ctx_set_errno() is also part of proj_api.h - I assume for the same reason" : I'm pretty sure this wasn't really intended to be used by applications, but just because there is no proj internal include file. That should be there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the proj errno numbers (which are negative) are quite specific (and actually much too specific in my opinion), but pj_strerrno()
supports the generic POSIX positive errno values as well. Especially I tend to use EINVAL (invalid argument) symbol for quite a lot of generic reporting. While not strictly C89 (I think only EDOM and ERANGE are C89 required), I am yet to see a platform where EINVAL is not defined.
Otherwise, I much agree that fighting for slim APIs is the good fight: Much of the chaos of PROJ.4 is the result of API sprawl. Obviously creeping featurism and clunky design is easier to realise in hindsight, than to prevent in foresight.
And care, good taste, real world experience, and a sharp razor are the right tools to ensure that an API stays fresh.
I just think my real world experience differ from yours wrt. error reporting: I have always found it very useful to be able to set an error level on a state object from user/application level code, and to try to make sure that system level code turned into no-ops when handed an error flagged state object.
So I would much prefer to keep pj_set_errno()
in the API. I would probably be more eager to move pj_context_errno_set()
to a proj_plumbing.h
(or whatever) internal system tools file.
Regarding the lack of a proj internal include file: Wasn't the idea of introducing proj_api.h
to demote projects.h
to "internal include" status?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I much agree that fighting for slim APIs is the good fight
Pleased to see we are aligned on this objective
I just think my real world experience differ from yours wrt. error reporting: I have always found it very useful to be able to set an error level on a state object from user/application
OK, if this is on purpose, let's go for it
Regarding the lack of a proj internal include file: Wasn't the idea of introducing proj_api.hto demote projects.h to "internal include" status?
Perhaps, but the issue is that projects.h was already a installed header (since it is include_HEADERS in https://github.com/OSGeo/proj.4/blob/master/src/Makefile.am#L9), so there's always a temptation from API users to use things even if marked as internal details. I really thing using a proj_internal.h that is not installed is the clean way of keeping internal details hidden reliably
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps, but the issue is that projects.h was already a installed header (since it is include_HEADERS in
It says in the top of projects.h
that it is to be considered private. It was excluded from include_HEADERS
in version 4.8, but was re-introduced with 4.9 because there was some complaints about missing functionality in proj_api.h
. I think it is fair to remove it from the public once again and rename it proj_internal.h
. Obviously not in the comming release, but the next as I suggested on the mailing list.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK. Looks like a good plan. Perhaps there should be a ticket to checklist those future actions so that we don't forget...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I have been thinking about setting up a project here on GitHub for the coming release. I'll try to play around with that today I think.
I don't think that GDAL would need that currently, but this is more a provision. Actually I had rather in mind a MapServer related situation where we might potentially have in the future x,y and z separated. |
src/proj.h
Outdated
@@ -287,14 +287,13 @@ PJ_OBS proj_trans_obs (PJ *P, enum proj_direction direction, PJ_OBS obs); | |||
PJ_COORD proj_trans_coord (PJ *P, enum proj_direction direction, PJ_COORD coord); | |||
|
|||
|
|||
int proj_transform ( | |||
size_t proj_transform ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggested to get rid of most, if not all, include files at top of proj.h, but the use of size_t requires #include <stddef.h>.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks - both for the original suggestion, and for this reminder about it. There really had accumulated a lot of cruft at the top. It is now much reduced
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great. What could help is to have a simple proj_test_include_proj_h.c that just does #include "proj.h" . I've done something similar recently in GDAL to check that public headers are really well self contained.
…tions from Even Rouault
I tried something like:
#include <proj.h>
#include <stdio.h>
int main (void) {
puts ("proj.h included successfully");
}
Which worked as suspected. Is this what you suggested?
2017-07-04 23:10 GMT+02:00 Even Rouault <notifications@github.com>:
… ***@***.**** commented on this pull request.
------------------------------
In src/proj.h
<#530 (comment)>:
> @@ -287,14 +287,13 @@ PJ_OBS proj_trans_obs (PJ *P, enum proj_direction direction, PJ_OBS obs);
PJ_COORD proj_trans_coord (PJ *P, enum proj_direction direction, PJ_COORD coord);
-int proj_transform (
+size_t proj_transform (
Great. What could help is to have a simple proj_test_include_proj_h.c that
just does #include "proj.h" . I've done something similar recently in GDAL
to check that public headers are really well self contained.
—
You are receiving this because you were assigned.
Reply to this email directly, view it on GitHub
<#530 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AFWTrUSJFhN8S7ZebSfCb53z5ENpuM3Vks5sKqoqgaJpZM4OHxvE>
.
|
Yes, You can even reduce to return 0; and not output anything ;-) |
OK - works flawlessly for all of projects.h, proj_api.h and proj.h
(compiling with -W -Wall -Wextra -pedantic), as they stand now. I do,
however, suspect that a few #includes can be removed from both projects.h
and proj_api.h :-)
2017-07-04 23:54 GMT+02:00 Even Rouault <notifications@github.com>:
… I tried something like: #include <proj.h> #include <stdio.h> int main
(void) { puts ("proj.h included successfully"); } Which worked as
suspected. Is this what you suggested?
Yes, You can even reduce to return 0; and not output anything ;-)
—
You are receiving this because you were assigned.
Reply to this email directly, view it on GitHub
<#530 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AFWTrZ4M02e7YJSCjUajLw884dt4tVe7ks5sKrSFgaJpZM4OHxvE>
.
|
…ssue, as another check has locked the path, but at least this should calm down MSVC
…uff from pj_obs_api.c to pj_internal.c
src/lib_proj.cmake
Outdated
@@ -228,6 +228,7 @@ set(HEADERS_LIBPROJ | |||
proj_api.h | |||
proj.h | |||
geodesic.h | |||
proj_internal.h |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will this not make proj_internal.h
public?
It will, and has been removed again. It was introduced intermediately while
debugging some intractable build system mysteries :-)
2017-07-05 16:37 GMT+02:00 Kristian Evers <notifications@github.com>:
… ***@***.**** commented on this pull request.
------------------------------
In src/lib_proj.cmake
<#530 (comment)>:
> @@ -228,6 +228,7 @@ set(HEADERS_LIBPROJ
proj_api.h
proj.h
geodesic.h
+ proj_internal.h
Will this not make proj_internal.h public?
—
You are receiving this because you were assigned.
Reply to this email directly, view it on GitHub
<#530 (review)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AFWTrWb13lQhBcvpQZ9OKTw-gpP8fdTiks5sK5-fgaJpZM4OHxvE>
.
|
src/pj_obs_api.c
Outdated
void pj_context_inherit (PJ *mother, PJ *daughter) { | ||
pj_set_ctx (daughter, pj_get_ctx (mother)); | ||
/* Create a new context - or provide a pointer to the default context */ | ||
PJ_CONTEXT *proj_context_create (int multithreaded) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't it be simpler to leave out the multithreaded
argument? If you want the default context you call proj_create(0, "...")
and if you want a non-default context you create it be calling proj_context_create()
. In case you want to prepare your application for multithreaded use, but is still running it singlethreaded a non-default context can be used with out problem:
PJ_CONTEXT * ctx proj_context_create();
PJ * P proj_create(ctx, "+proj=...");
...
proj_context_destroy(ctx);
proj_destroy(P);
To me that is a little bit cleaner and simpler.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree - and it comes with the extra feature of being easier to document and demonstrate :-)
Will change that, and update the demo samples accordingly.
Right now there are two prefixes in play: |
Coherency is for tailors :-) Switching to a function implementation would make it natural to move it to the proj_ namespace, and furthermore probably would allow us to remove the Seems worthwhile - will implement during the afternoon. |
src/proj.h
Outdated
int proj_errno_reset (PJ *P); | ||
void proj_errno_restore (PJ *P, int err); | ||
|
||
char *proj_get_definition (PJ *P); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To avoid potential cross heap issues on Windows (if proj is compiled with MSVC X and the application integrating it with MSVC Y), we should provide a proj_free() (if that name is still avaialble) that does the free inside the lib.
( with the "old" proj_api.h, this was the pj_get_def() / pj_dalloc() tuple)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for spotting this! It could result in some really nasty debugging sessions. In a recent commit, I have added the function proj_buffer_free, as (mostly) a synonym for pj_dealloc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the tests pass, definitely ;-) I'll probably review once again all changes once they are committed.
Remark: It doesn't look like proj_get_definition and proj_buffer_free are added to proj.def. The maintenance of this file is really painful. Wondering if we shouldn't have a PROJ_EXPORT macro that would be used in the .h directly and would expand to __declspec(dllexport) on Windows, but that's unrelated with this PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, go ahead when all tests pass :)
…tential cross heap issues on Windows. Thx to Even Rouault for spotting this
Oops - and I forgot a few more as well. Correcting, committing and pushing
in a few minutes :-)
2017-07-06 14:47 GMT+02:00 Even Rouault <notifications@github.com>:
… ***@***.**** commented on this pull request.
------------------------------
In src/proj.h
<#530 (comment)>:
> void proj_errno_set (PJ *P, int err);
-/* reduce some mental impedance in the canonical reset/restore use case */
-#define proj_errno_restore(P, err) proj_errno_set ((P), (err))
+int proj_errno_reset (PJ *P);
+void proj_errno_restore (PJ *P, int err);
+
+char *proj_get_definition (PJ *P);
If the tests pass, definitely ;-) I'll probably review once again all
changes once they are committed.
Remark: It doesn't look like proj_get_definition and proj_buffer_free are
added to proj.def. The maintenance of this file is really painful.
Wondering if we shouldn't have a PROJ_EXPORT macro that would be used in
the .h directly and would expand to __declspec(dllexport) on Windows, but
that's unrelated with this PR
—
You are receiving this because you were assigned.
Reply to this email directly, view it on GitHub
<#530 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AFWTrSee2T8szBe8pxmOFxrgatIHdKEYks5sLNdhgaJpZM4OHxvE>
.
|
… removed some not-yet-implemented material from proj.h
src/proj.h
Outdated
@@ -341,6 +341,9 @@ void proj_errno_restore (PJ *P, int err); | |||
|
|||
char *proj_get_definition (PJ *P); | |||
|
|||
/* deallocate anything obtained from PROJ.4 */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
anything... except a PJ* that should be destroyed with proj_destroy()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes - will make that clear when getting around to writing the formal documentation, and update the comment for clarity
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... actually - for clarity and API symmetry with the PJ_CONTEXT and PJ cases, I changed the two names to proj_definition_create() and proj_definition_destroy(). It may become necessary with API level access to a generic low level deallocator, but right now its only use is to get rid of material produced by proj_definition_create
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hum proj_definition_create() is a bit confusing for something that is essentially a getter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Down at the library level it is not exactly a getter (that could just be the case of returning P->params) ... "create" is much in accordance with the actual construction work done by pj_get_def: It actually walks the P->param list, element by element expanding the return string.
Now, the user does not need to know this, but for reasons of communication, I believe it is a good thing to have the reminder that "what you get by a constructor, you get rid of by a destructor"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, @rouault and @kbevers : What's to be done here? I seriously dislike the first solution: While proj_get_definition()
/ proj_buffer_free()
is a fairly mindless 1:1 translation from the original pj_get_def()
/ pj_dalloc()
implementation, it is totally orthogonal to current semantics of the proj_xxx API.
I can think of the following solutions:
-
As-is: A create/destroy pair, that clearly signals that what's created must be destroyed - when you start a paranthesis, you must also provide a corresponding end paranthesis at the proper syntactic and semantic point. I evidently much prefer this solution.
-
As-was: The original
proj_get_definition()
/proj_buffer_free()
pair, where the constructor/destructor pair is referentially asymmetric, where the constructor is specific, while the destructor is generic, and where, to add insult to injury, the naming is orthogonal to everything else in the proj_xxx API. I very much dislike this solution. -
As-maybe-to-be: A not-really-getter-but-not-either-referentially-a-getter: We could introduce a
proj_definition()
function, which would be referentially in alignment with theproj_error()
function, but would need a low level deallocator, which would be referentially assymetric to the constructor/pseudogetter, but which, on the positive side, could be play the role of a C++ default-destructor. In acknowledgement of the humble low-level building bricks of PROJ.4, I would prefer to call this functionproj_dealloc()
. I could certainly live reasonably happily with this solution, although I would very much prefer to make it more clear when something is constructed for the API user, and hence also should be destroyed by the API user.
There is probably at least half-a-dozen additional potential solutions, that I cannot think of right now - but have a go: Suggestions? Opinions?
…e/proj_definition_destroy, for symmetry and clarity
Repeating this comment at a more reasonable place in the thread: So, @rouault and @kbevers : What's to be done here? I seriously dislike the first solution: While I can think of the following solutions:
There is probably at least half-a-dozen additional potential solutions, that I cannot think of right now - but have a go: Suggestions? Opinions? |
Another possibility could be a I don't want to be punctilious, so basically I let it to your judgement for the best solution. This is just the naming create_definition() which is a bit weird at first since the PJ* object itself was created from a definition that is close to the one that will be returned. |
I see your point - in many ways it is parallel to my introduction yesterday of With this in mind, I could imagine a somewhat parallel, but more general, case where we introduce the suffix pair We might even try to make do with a single
The two latter will require some future care, to make sure that we never introduce |
(extended/continued)
With an overall semantic structure saying that
Hence a structure, where create/delete governs the life cycle of state objects, retrieve/release governs the life cycle of material derived from state objects, that requires allocation on the heap, while get-style functions return material on the stack, that is either a reflection of internal state (fields), or derived from these. I did a test implementation using Obviously, I resisted the temptation to define a |
I like this one.
I think this structure makes sense. Are you suggesting a generic |
Yes - stuff that can be free'd by a default destructor can be retrieved (other stuff must be constructed, and a type specific destructor must be called). And since we may call the default constructor whatever we want, I will follow your vote and rename proj_free to proj_release, wait for tests to pass, and then squash+merge. |
The OBS API has been under development for about 12 months. The main intention is to provide access to functionality for dynamic datums and multistep transformations. A secondary intention is to improve name spacing and modularity of the code base.
This PR reflects a recent restructuring of the API architecture following input from discussions on the PROJ.4 mailing list. The material is still in a state of flux - among other things, the (sparse) documentation still refers to an older version of the API, so until it has been updated, it is not expected to make much sense.