T O P I C R E V I E W |
RickHodgin |
Posted - Apr 14 2007 : 10:42:11 PM I have an occasional need for string lengths to be auto-computed. If there was a way to have VAX do that it would be great.
Something like this: as I'm typing this expression: if (memicmp(some_ptr, "some constant", _
When to the _ part and VAX would have the length of the constant string immediately before as one of the options available for selection and auto-population, like when auto-completing the member function definition data from the header.
It would complete only the "13" part, as in: if (memicmp(some_ptr, "some constant", 13) == 0) |
30 L A T E S T R E P L I E S (Newest First) |
feline |
Posted - Nov 09 2019 : 1:45:16 PM The older versions of Visual Studio have a built in macro feature already. Sadly Microsoft decided to remove it, apparently because virtually no one ever used it. I made good use of it, but I know from using IDE macros to solve problems for our users that macro use was a very rare feature among our users.
The fact that the macros had to be written in VBScript may well have been a factor.
Currently we don't have any plans to add Macro support to Visual Assist. A quick search brought up this extension for doing IDE macros, but there are others out there:
https://marketplace.visualstudio.com/items?itemName=XavierPoinas.TextMacrosforVisualStudio201220132015
I haven't used any of these yet, so I don't know how well they work, but I do see the appeal and usefulness. |
foxmuldr |
Posted - Nov 07 2019 : 07:59:58 AM Here's an example of how the SAL macro language works. Global variables. Functions. Main(). Convenient interfaces with standard abilities like key definitions and screen attributes.
/****************************************************************************
Crosshair 1.0
Michael Durland
[email protected]
May 20, 2004
This macro provides a movable crosshair on the screen which makes it easy
to line up text vertically and horizontally. For example, if the cursor
is near the bottom of the screen, it can be used to visually identify
what is directly above the cursor near the top of the screen. This is
also useful across multiple open windows for visually checking lineup.
The crosshair can be moved around the screen using the cursor keys.
The ENTER key will remove the crosshair and set the cursor to the
crosshair location. Any other key will remove the crosshair and restore
the cursor to its original position.
The macro is most useful when assigned to a hotkey of your choosing.
Feel free to use/modify/distribute/etc. however you want.
This macro was developed under version TSE Pro v4.0.
Portions of this macro are based on ruler.s by Glenn Alcott.
*****************************************************************************/
integer start_column = 0
integer start_line = 0
integer start_x = 0
integer start_y = 0
integer cross_x = 0
integer cross_y = 0
integer edge_top = 1
integer edge_bottom = 0
integer edge_left = 1
integer edge_right = 0
integer cross_color = Color(bright black on blue)
// Other interesting colors:
//integer cross_color = Color(bright red on blue)
//integer cross_color = Color(bright blue on blue)
//integer cross_color = Color(bright cyan on blue)
//integer cross_color = Color(bright yellow on blue)
//integer cross_color = Color(bright magenta on blue)
//integer cross_color = Color(white on blue)
//integer cross_color = Color(red on blue)
proc paint_crosshair()
integer i = 0
string s[1] = ""
UpdateDisplay(_ALL_WINDOWS_REFRESH_)
// Paint the horizontal line
for i = edge_left to edge_right
GetStrXY(i, cross_y, s, 1)
// Only paint line in blank spaces, and skip the crosshair intersection
if Asc(s) == 32 and i <> cross_x
PutOEMStrXY(i, cross_y, Chr(196), cross_color)
endif
endfor
// Paint the vertical line
for i = edge_top to edge_bottom
GetStrXY(cross_x, i, s, 1)
// Only paint line in blank spaces
if Asc(s) == 32
if i == cross_y
// Paint the intersection
PutOEMStrXY(cross_x, i, Chr(197), cross_color)
else
PutOEMStrXY(cross_x, i, Chr(179), cross_color)
endif
endif
endfor
// Show the current crosshair position in the status bar
Message("L ", start_line + cross_y - start_y : -8, "C ", start_column + cross_x - start_x)
end
proc Main()
integer cursor_state = Query(Cursor)
integer done = FALSE
Set(Cursor, OFF)
// Save current cursor position in buffer
start_column = CurrCol()
start_line = CurrLine()
// Save absolute starting position
start_x = WhereX()
start_y = WhereY()
// Initialize absolete crosshair position
cross_x = WhereX()
cross_y = WhereY()
// Initialize crosshair width range
if Query(DisplayBoxed)
edge_left = 2
edge_right = Query(ScreenCols) - 1
else
edge_left = 1
edge_right = Query(ScreenCols)
endif
// Initialize crosshair height range
// Note these may be slightly off depending on current customizations.
edge_top = 3
edge_bottom = Query(ScreenRows) - 2
// Do it
repeat
paint_crosshair()
case GetKey()
when <CursorDown>
// Move down one row
cross_y = iif(cross_y >= edge_bottom, edge_bottom, cross_y + 1)
when <CursorUp>
// Move up one row
cross_y = iif(cross_y <= edge_top, edge_top, cross_y - 1)
when <CursorLeft>
// Move left one column
cross_x = iif(cross_x <= edge_left, edge_left, cross_x - 1)
when <CursorRight>
// Move right one column
cross_x = iif(cross_x >= edge_right, edge_right, cross_x + 1)
when <Home>
// Move to the left edge of the screen
cross_x = edge_left
when <End>
// Move to the right edge of the screen
cross_x = edge_right
when <PgUp>
// Move to the top of the screen
cross_y = edge_top
when <PgDn>
// Move to the bottom of the screen
cross_y = edge_bottom
when <Enter>
// Exit the crosshair mode, setting the cursor to the
// current crosshair location.
GotoRow(CurrRow() + (cross_y - start_y))
GotoColumn(start_column + cross_x - start_x)
done = TRUE
otherwise
// Exit the crosshair mode, restoring the original
// cursor position.
done = TRUE
endcase
until done
UpdateDisplay(_ALL_WINDOWS_REFRESH_)
Set(Cursor, cursor_state)
end
|
foxmuldr |
Posted - Nov 07 2019 : 07:43:23 AM quote: Originally posted by feline Not as easy as having this built into VA, but I am not sure how well this would really fit into VA. It seems a very specific command.
Counting a string's length at development time is a fundamental ability that should exist in a programmer's toolkit IMO. You do not know why people might use it. People create data lists that have to be navigated quickly by pointers. They code in assembly at times, where that information is needed. I literally use it scores of times per year, and each time now is a manual effort. I go to the start, hold down shift to select, and arrow over in fives.
Certain abilities should be given to developers. They should be able to figure out what's right for them.
I use a text editor called The SemWare Editor. It's older, made back in the 80s and 90s originally, but it's been maintained by the developer and he still releases periodic updates to it.
It has a built-in macro language called SAL, which is Pascal-like. It is able to access the whole host of Win32 features, plus custom DLLs. If you download the test drive version and use the menu, you can see sample macros under Potpourri. It comes with some very handy macros. One is TabUtil, which will entab or detab a file, converting it to spaces or tabs based on the editor's tab width (typically 4).
Sample macros: https://semware.com/html/tseprofilesr.php
There, you can write your own macro code in their SAL language, performing logic on the editor lines of content, environment, and do real work. People have written games in SAL. And it has access to reach out to custom DLLs allowing for a simple interface / front-end for graphics windows you create. It also has built-in macro support for creating on-the-fly macros.
I wrote a macro back in the day to count strings like this. A quick two-key and it inserts the count as selected text into the source file so I can Ctrl+X and move it to where I need. All I have to do is be somewhere between the left " and the right " (also works with ' characters as some of the programming languages I code in allow " or ').
Maybe VAX could introduce a little macro language. It could include contextual global variables like line, column, which would indicate where the caret is. And we could programmatically use VAX callbacks to access functions like line_prev(), line_next(), update_line() and so on, and perform read-only processing of the data, with the one update_line() function existing to update it.
We could gain access to the clipboard via set_clip() or get_clip() functions. Use variables that are character, integer (8-byte int), or floating point (double). Make it very simple, but have enough basic string abilities to perform useful processing on those code lines.
Provide enough simple abilities, and many of the features we ask for could be community-written, without any new VAX support or features. |
feline |
Posted - Nov 06 2019 : 2:47:48 PM That is an old post by me you quoted But the memories have stuck. These days I tend to use an array of strings, and an enum for referencing them. So here it would be something like "options" for the array, and "OPT_SOME_EXAMPLE" to reference "some example" entry in the list.
Yes, it is more characters to type, but it also means that the string is only written once, and you can do a Find References on the enum, to reliably find all of the references. I have only grown more wary of hard coded strings over time, since they never seem to stay static forever, at least not for me
Thinking about how to easily do this now though, you can use Alt+Shift+] to use VA's Smart Select to easily select the entire string. Then personally I would use Auto Hot Key, a small Windows utility I already use for hot keys and general keyboard commands, to set a command that changes the clipboard value to the string length of the current clipboard:
https://www.autohotkey.com/ https://www.autohotkey.com/docs/misc/Clipboard.htm
making it easy to paste in the length a moment later.
Not as easy as having this built into VA, but I am not sure how well this would really fit into VA. It seems a very specific command. |
foxmuldr |
Posted - Nov 06 2019 : 1:38:11 PM quote: Originally posted by feline
If I might be permitted a small rant?
A few years ago while working under UNIX (so nothing to do with Whole Tomato) I spent 2 solid weeks tracking down "my program crashes", only to eventually find the cause was the code:
char *pBuffer = (char *)malloc(6);
strcat(pBuffer, "hello");
I had a similar bug in one of my apps. I had adapted a library from my personal code, and at some point I modified an "iAllocateBlock(int nLength)" function to allocate exactly the nLength bytes as indicated. However, one particular function used it and expected it to auto-allocate nLength + 1 for the trailing NULL.
It literally took me 4 years to track down that bug, working on it here and there. I had a work-around by changing my malloc() function to auto-allocate nLength + 16 at each use. It was clunky, but it fixed it. And I used endless memory mapper / debugger / runtime / compile-time functions to try and track down the bug and none of them found it. I finally wrote my own malloc(), free(), et al functions, and was able to track it down myself because of the trailing NULL character wrote into an area of memory that had a pattern in it that highlighted the error.
That was a great day. :-)
quote:
After being the "lucky" person to fix a various other bugs caused by people overrunning their buffers by 1 character - often doing things like your piece of code, I was given the task of working through the entire code base, by hand, studying and removing all calls to sprintf, strcat, and their relatives.
Trust me on this one, hard coding the length of the string is a bad idea, unless your strings are SO set in stone they can NEVER, EVER, EVER change. Experience indicates this is very unlikely, even when people claim it is true.
Now back to your regular viewing experience
I (foxmuldr = RickHodgin) could still use this feature to this day.
The times I use it are for processing command-line options, or for ad hoc data interchange between systems (using sockets). When a message is received, I find it more desirable to write this:
if (strlen(option) >= 12 && _memicmp(option, "some example", 12) == 0)
{
// Code here
}
than this:
const char cgcSomeExample[] = "some example";
if (strlen(option) >= sizeof(cgcSomeExample) - 1 && _memicmp(option, cgcSomeExample, sizeof(cgcSomeExample) - 1) == 0)
{
// Code here
}
I could convert it to a string, but it's only used there ... so why bother?
Just happened across this today searching for my old account. And I could literally use this function regularly for writing systems that process command line parameters, or process messages sent from other systems.
|
sl@sh |
Posted - Apr 25 2007 : 03:35:15 AM I wouldn't consider myself a 'non-C programmer' although I switched to C++ some 20 years ago. your define still makes me cry. A clean solution that doesn't even need a #define statement would be an inline function like this:
inline int my_memicmp(const void* some_ptr , const char* some_constant_string)
{
return memicmp (some_ptr, some_constant_string, strlen(some_constant_string));
}
// ...
if (my_memicmp (some_ptr, "some constant") == 0) You could actually make the first function parameter a const char* as well if that fits your code, but in that case you could just use strcmp instead (you might need to use your string constant as the first argument if you're not sure some_ptr actually has a 0-byte at the end). It would certainly reduce the potential of introducing errors later.
P.S.: you can of course use sizeof(...)-1, provided the project doesn't use multibyte characters (but in that case memXXX functins will cause problems anyway) |
mmb |
Posted - Apr 24 2007 : 6:15:57 PM To reply to the initial posting, try this:
#define _L(txt) txt, sizeof(txt)-1
if (memicmp(some_ptr, _L("some constant")) == 0)
Pro: - does recompute the length at compile time (most compilers do NOT optimize strlen("xxx") into 3) - works on other places as well Cons: - works ONLY for string constants (but creates hard to find errors otherwise) - a macro with comma inside let some non-C programmers cry ;-) |
netics |
Posted - Apr 24 2007 : 04:47:53 AM Totally agreed on feline. I don't want to see that VA support features for the bad coding convention. |
RickHodgin |
Posted - Apr 19 2007 : 1:55:43 PM quote: One nicely detailed email sent.
One nicely detailed response sent. |
feline |
Posted - Apr 19 2007 : 1:30:25 PM One nicely detailed email sent. |
RickHodgin |
Posted - Apr 19 2007 : 10:30:14 AM feline, can you email me so I can discuss this with you? |
feline |
Posted - Apr 19 2007 : 09:41:09 AM I have some experience producing formatted text, and based on this I would suggest you want to write some simple functions, and add some variables, or #defines.
I am thinking about:
#define COLUMN1 1 #define COLUMN2 30 #define COLUMN3 50
from these you can easily work out the width of the columns. Then you "just" need the functions:
void addTextToColumn(int nValue, int nColumn); void addTextToColumn(const char *pszText, int nColumn); void addTextToColumn(float fValue, int nColumn);
A certain amount of work to setup, but if done carefully, and used carefully, a very successful approach.
Rick I have used the above approach to take the same input data and produce either a formatted documents on a dumb terminal or a table based HTML report, of the same information, based on the current settings. This approach, with time and care, scales very well, and I suspect takes a *lot* less work to get up and running than an IDE plugin, or plugin into VAX. Certainly it is a lot more flexible, since you can change the output format, as I did when I added HTML and XML for XSL processing, without changing the API. |
RickHodgin |
Posted - Apr 19 2007 : 08:59:38 AM quote: I just realized however, that short of interpreting manipulators (i. e. setw(int)) VAX would have little to no means of really be of help here, so I suppose my point is moot
I suppose if we can get feline to discuss the VAX API idea, then you could write whatever you need for your custom request, and it would be done through VAX. |
sl@sh |
Posted - Apr 19 2007 : 03:31:59 AM @feline: Yes, this is what I do myself - but unfortunately the text is often composed of bits and pieces contained in variables. I just realized however, that short of interpreting manipulators (i. e. setw(int)) VAX would have little to no means of really be of help here, so I suppose my point is moot |
feline |
Posted - Apr 18 2007 : 08:08:19 AM Rick, sometimes it is hard to know what people really think I have encountered people who do not recognise the risk of errors in their own code, so the concept that "my code is perfect" is unfortunately one that does exist *rolls eyes*
Or even worse the approach of "error handling? I will just call abort()". That actually worked when the code was a stand alone command line utility, but not so well when the code was compiled into the middle of a rather large GUI program... So sometimes "it was obviously a joke" fails, since I have had to clean up after people doing that for real *sigh* not your fault
Syntax highlighting on the current line when you have the background highlighted, there is a feature request for this, but it is not a high priority change at this time.
sl@sh, when I have a similar problem I tend to do something like this:
static void formatReportText(...)
{
char *pszLineOne =
"this is the text formatted into\\n";
char *pszLineTwo =
"columns using a rather low tech\\n";
char *pszLineThree =
"method\\n";
}
Syntax highlighting, plus having the caret position (line and column) listed on the status bar help this to work. Not so nice to work with, but I keep blocks like this well isolated, and they make the formatting task so much easier! |
sl@sh |
Posted - Apr 18 2007 : 04:13:04 AM Talking about (string) lenghts, while I don't see any real need for the example given above, what I would like to see are functions that help me keep count of text positions for any kind of formatted text output. For a start the ability to select a given string or part of it and seeing the selected string's (or part's) length somewhere (as a tooltip?) might be very helpful.
I know I would have liked such a feature every time I tried to do some formatted table-like text output. Instead I had to manually count characters to make sure all lines of output are aligned correctly. |
RickHodgin |
Posted - Apr 17 2007 : 11:45:32 AM quote: However this rather assumes that the hard coded length is close to the string...
This is true. The times I would use this would usually be when I am writing code like I showed above, or if I'm doing assembly programming where I need to build a type of quick-parse tree and use a format which builds a variable-length link-list at compile/assemble time.
quote: When the hard coded length is in a different cpp file, 8 directories away, in a totally different directory tree, 16 function calls away...
Yes, I have been there, done that, got the t-shirt.
I'd throw that T-shirt away! Code like that always makes me cringe. I'm actually working on a project like that right now. Hastily written, no real documentation, a horrible class structure. It's taken me quite some time to become reasonably fluent in this code.
My mindset about how to write code comes primarily from the years I spent writing almost exclusively in x86 assembly. I learned how to organize things to make them very straight-forward. I also learned how to utilize editor function to create easily traversible data structures without such complex mechanisms as maps, lists, vectors, etc. I even wrote a complete 32-bit operating system, though I never wrote any real software for it.
In any regard, I didn't mean to suggest seriously that I do not write errant code. I do. There are several things I occasionally write without having errant code, but little errors are almost always still there (upper-case this, lower-case that, . instead of ->, etc, etc, etc.
I appreciate all of your responses to my posts. Know that I don't always expect a response. The one thing above all else I would like to see is syntax highlighting on the solid-color cursor line. But, I am likely the only one. :) It come from The SemWare Editor and its syntax highlighting abilities. It had an option for turning it on or off, and I just got used to on. |
RickHodgin |
Posted - Apr 17 2007 : 09:13:54 AM quote: Rick you may not write errors into your code
Of course I do. We all do. Daily even. I thought that joke was so blatently obvious (hence my use of the big-eyed emoticon --like I was standing there with a near smile on my face and big eyes to indicate what I had just said was a joke). Still, I apologize to everyone. |
feline |
Posted - Apr 17 2007 : 07:54:10 AM The code sample I posted was to show the effect. In the real code the string being added was run time generated, so you never knew its length in advance.
sl@sh is correct, the bug occurred at random, since some of the time the allocated memory had a null as the first character, but not always. Surprisingly enough the code ran, live, for years without causing a problem, and only tended to crash reliably for one very specific task.
The crash occurred when the string did not start with a null, and then the buffer was overrun by a random number of characters, normally just one.
Rick you may not write errors into your code, but do you work alone? Ever worked in a company along side a trainee programmer? Or used an API with zero documentation and confusing examples?
quote: Originally posted by RickHodgin
This would probably be true unless you happened to have an editor where you hit a one-key or two-key function to update the length for you if you happened to change the string.
I was using VIM, the key command you want is CTRL-A or CTRL-X. Something that I still miss.
However this rather assumes that the hard coded length is close to the string... When the hard coded length is in a different cpp file, 8 directories away, in a totally different directory tree, 16 function calls away...
Yes, I have been there, done that, got the t-shirt.
On that occasion, a completely different bug, the hard coded string length was a buffer used to compose an error message, it was 2k in size and never checked, since no one would ever need more than 2k to write an error message...
Except there were two error logging functions, one with a 2k buffer and one with an 8k buffer, and when generating SQL INSERT statements with XML fields it is surprisingly easy to generate a 9k string...
Of course the 4k string was worse, since it crashed at random, since the code called one or other error logging function, at random, since people did not know that they had different buffer sizes inside of them. The functions were 10 years old and the authors had long since left the company. |
sl@sh |
Posted - Apr 17 2007 : 07:44:42 AM quote: Originally posted by RickHodgin
quote: I spent years learning the hard way why defensive programming is a good idea
I've heard silly people say this kind of thing for years. But since I almost never write errors into my code... I mean really I just don't see the need.
And nobody else but you ever touches the code you write either? Wow, you are one lucky guy. (I would say one in a million, but that would likely be an understatement ...)
Never make assumptions about the code you write today. You will be proven wrong. Eventually. |
sl@sh |
Posted - Apr 17 2007 : 07:35:12 AM I wouldn't expect the malloced memory to possess any particular value before it's initialized. Or, to be more precise, I would expect it to be anything but 0.
On a sidenote, I agree with feline that using strlen() would be the easiest and cleanest way to solve the initial problem. |
mwb1100 |
Posted - Apr 17 2007 : 12:07:44 AM quote: Originally posted by RickHodgin
That code you had looks like it should work. strcat() would attempt to append "hello\\0", a 6-character string on to the end of a 0-length string (the initialized malloc() block). Why would that cause an error?
Who initialized the block provided by malloc()? From the C99 standard doc: "The malloc function allocates space for an object whose size is specified by size and whose value is indeterminate"
Though, I suspect that feline actually meant to have the example not account for the terminating NULL in the block allocation - but introduced an 'off-by-one' error in the direction of not being a bug (just guessing). |
RickHodgin |
Posted - Apr 16 2007 : 10:04:45 PM quote: The problem with these bugs, this one included, is that they did NOT crash at, or even close to, the bad code, so the call stack was dubious.
That code you had looks like it should work. strcat() would attempt to append "hello\\0", a 6-character string on to the end of a 0-length string (the initialized malloc() block). Why would that cause an error?
quote: I spent years learning the hard way why defensive programming is a good idea
I've heard silly people say this kind of thing for years. But since I almost never write errors into my code... I mean really I just don't see the need.
quote: hard coding the length of the string is a bad idea, unless your strings are SO set in stone they can NEVER, EVER, EVER change.
This would probably be true unless you happened to have an editor where you hit a one-key or two-key function to update the length for you if you happened to change the string. It's certainly something the developer must remember, but it's still doable and, provided you have such tools at your disposal, it's not quite the difficult thing it would be otherwise. It's just a thing to do.
But, I agree. Hard-coding anything is not a good idea. |
feline |
Posted - Apr 16 2007 : 5:55:05 PM quote: Originally posted by RickHodgin
quote: only to eventually (two weeks later) find the cause was the code:
Fortunately with VS we have good debuggers with a call stack trace. :)
Again, speaking from those days, getting a call stack was something we did so often there was a UNIX shell script to do the job for us. Crashing programs while developing was fairly normal
The problem with these bugs, this one included, is that they did NOT crash at, or even close to, the bad code, so the call stack was dubious. Often the call stack was reported that the process crashed while crashing
I spent years learning the hard way why defensive programming is a good idea |
mwb1100 |
Posted - Apr 16 2007 : 5:48:05 PM quote: Originally posted by RickHodgin
quote: only to eventually (two weeks later) find the cause was the code:
Fortunately with VS we have good debuggers with a call stack trace. :)
I'd guess that not many people would like the idea of adding something to VAX that increases the need to use the debugger.
Might as well have it add the /* BUGBUG */ comment when it inserts the length value.
|
RickHodgin |
Posted - Apr 16 2007 : 5:28:32 PM quote: only to eventually (two weeks later) find the cause was the code:
Fortunately with VS we have good debuggers with a call stack trace. :) |
feline |
Posted - Apr 16 2007 : 5:12:39 PM If I might be permitted a small rant?
A few years ago while working under UNIX (so nothing to do with Whole Tomato) I spent 2 solid weeks tracking down "my program crashes", only to eventually find the cause was the code:
char *pBuffer = (char *)malloc(6);
strcat(pBuffer, "hello");
After being the "lucky" person to fix a various other bugs caused by people overrunning their buffers by 1 character - often doing things like your piece of code, I was given the task of working through the entire code base, by hand, studying and removing all calls to sprintf, strcat, and their relatives.
Trust me on this one, hard coding the length of the string is a bad idea, unless your strings are SO set in stone they can NEVER, EVER, EVER change. Experience indicates this is very unlikely, even when people claim it is true.
Now back to your regular viewing experience |
RickHodgin |
Posted - Apr 16 2007 : 4:19:23 PM I like the ToolTip idea. :) |
feline |
Posted - Apr 16 2007 : 07:44:24 AM Speaking as someone who spent a lot of years working in C, this is a bad idea. As soon as you change the length of the string the hard coded number breaks, which can lead to nasty bugs. I know, I have had to come along afterwards and clean them up *shudder*
Experience indicates relying on people updating the number by hand is not a good solution either, it is to easy to miss it.
I would recommend:
const char *pszValue;
memicmp(some_ptr, pszValue, strlen(pszValue)
If this is cumbersome you could consider wrapping memicmp in a macro or function call to do the strlen for you. |
sl@sh |
Posted - Apr 16 2007 : 04:10:12 AM Hmm, considering people nowadays use the std::string class that wouldn't need such kind of information I'm not sure many people would appreciate such a function, especially in the limited way you describe. If anything I could imagine a hovering tooltip that displays a string's length when you hover the mouse over a string value. Whether or not anyone uses that value is up to them, but it at least will save you the manual counting. |
|
|