You are on page 1of 18

Coding Info - Collected from different sources

1.) Source -

Description : Hide Function Pointer Declarations With a typedef

Can you tell what the following declaration means?

void (*p[10]) (void (*)() );
Only few programmers can tell that p is an "array of 10 pointers to a function returning void and
taking a pointer to another function that returns void and takes no arguments." The cumbersome
syntax is nearly indecipherable. However, you can simplify it considerably by using typedef
declarations. First, declare a typedef for "pointer to a function returning void and taking no
arguments" as follows:

typedef void (*pfv)();
Next, declare another typedef for "pointer to a function returning void and taking a pfv" based on
the typedef we previously declared:

typedef void (*pf_taking_pfv) (pfv);
Now that we have created the pf_taking_pfv typedef as a synonym for the unwieldy "pointer to a
function returning void and taking a pfv", declaring an array of 10 such pointers is a breeze:

pf_taking_pfv p[10];
Danny Kalev

2.) Algorithm that you will mesmerize
Source -
Desc : Finding Modulus of a Very Large Number with a Normal Number
I recently encountered this problem in a C++ program running on a 32-bit UNIX platform. The
problem was that the number was 28 digits long and no native datatype in C++ could store a
number of that sizenot even unsigned long long or long double could help; they were only
eight bytes long.
A divide-and-conquer strategy can be used in this casethe entire number doesn't have to be
processed in one go. Divide the number into smaller numbers, then find the remainders of these
smaller numbers. Add up the remainders and repeat the process. As long as the remainders are
retained, the final answer wont change.
Suppose you need to find nBig % a:
1. Take a manageable portion (say, x) of the input large number (nBig) and subtract it from
nBig (nBig = nBig - x).
2. Compute the mod of x (mod = x % a).
3. Add the mod to nBig (nBig = nBig + mod). This way, the number nBig can be reduced
without affecting the actual answer.
This first example uses small numbers, so it's easy to follow the logic. To find 27 % 8:
1. Let x = 17. Thus, nBig = nBig - x = 27-17 = 10.
2. mod = x % a = 17 % 8 = 1
3. nBig = nBig + mod = 10 + 1 = 11
4. Thus, nBig is reduced from 27 to 11 without affecting the answer, which is 3.
But what about finding the mod of large numbers? Extend the same logic, and the above problem
can be solved as follows:
1. Let the input be:
o nBig: This is a string representing a 30 digit large number (for example,
o a: Integer (for example, 320)
2. To find nBig % a:
1. Iteration 1:
1. Extract few digits from the LHS of nBig. Thus, tmp = 1234567890 and
nBig = 33333333335555555555.
2. Find mod of tmp:
4. mod = tmp % a = 210
5. Prefix mod to nBig:
7. nBig = 21033333333335555555555
2. Iteration 2:
1. tmp = 2103333333 and nBig = 3335555555555
2. mod = tmp % a = 213
3. nBig = 2133335555555555
3. Iteration 3:
1. tmp = 2133335555 and nBig = 555555
2. mod = tmp % a = 195
3. nBig = 195555555
4. Iteration 4:
1. tmp = 195555555 and nBig = ""
2. mod = tmp % a = 35
5. The Answer = 35.
What follows is the pseudo code for finding nBig % a. First, let d = the number of digits that can
be processed at a time. Then, repeat while val( nBig ) >= a.
1. tmpStr: Equals d digits from the LHS of nBig.
2. nBig: Equals the remaining portion of nBig.
3. tmpNum: Equals toInteger(tmpStr).
4. tmpNum: Equals tmpNum % a.
5. tmpStr: Equals toString(tmpNum).
6. nBig: Equals tmpStr + nBig.
To select d, make sure d satisfies the following constraint: The maximum number of digits in
Mod a < d < should equal the maximum tmpNum minus the maximum number of digits in Mod.
For example, suppose you have a = 512 (Mod ranges 0..511), and your tmpNum can store a
maximum of 16 digits. Then, you'd have:

3 < d <= 16-3)
In another example, suppose a = 100 (Mod ranges 0..99), and tmpNum can store a maximum of
16 digits. In that case, you'd have:

2 < d <= 16-2)
Sameer Palande
3.) source -
Description Retrieving Only Unique Elements from Two Sets

This algorithm helps you to retrieve all the elements contained in the first set (set1), but not in
the second (set2).
Here's the template:

template<class T>
void Diff(const T& set1, const T& set2, T& res) {
T temp;
typename T::const_iterator it = set1.begin();
if (&set1 == &set2)
else {
while (it != set1.end()) {
if (set2.find(*it) == set2.end()) // element not found
Mykola Rozhok
4.) Source -
Description Use the Count Algorithm to Perform Simple Validation on
Sometimes it's necessary to perform a simple validation on a string to ensure, for example, that a
string destined to be converted to a float contains only a single '.'.
If only simple validation of some kind is required, it's trivial to use the count algorithm to tally
up suspected chars and report their numbers, as in:

#include <iostream>
#include <algorithm>
#include <string>

using namespace std;

int main()

string n = "joebob@bigtrucks@com";
string d = "12.34";

int A = count(n.begin(), n.end(), '@');
int C = count(d.begin(), d.end(), '.');

cout << "There are " << A << " @ signs in JoeBob's email address!" <<
cout << "There's " << C << " decimal point in JoeBob's checking
<< endl;

return 0;
It's nice to remember that strings are containers in the STL, just like a vector or list, and that their
iterators refer to char pointers that can be counted, sorted, or partitioned just like any other
collection of values.
Greg McClure
5.) Source -
Description: The reverse() Algorithm
Another useful STL algorithm is reverse(). This algorithm reverses the order of elements in a
specified sequence. reverse() takes two iterators that mark the sequence's beginning and end,
respectively. Here is an example of reversing the order of a char array:

#include <algorithm>
#include <vector>
using namespace std;
int main()
char arr[4] = {'a','b','c','d'};
reverse(arr, arr+4); // arr is now "dcba"
Remember that the second argument of reverse() must point one element past the array's bounds.
Here's an example of applying reverse() to a vector object:

int main()
char arr[]= {'a','b','c','d'};
vector <char> vc (arr, arr+4); // initialize vc
reverse(vc.begin(), vc.end());
Danny Kalev
6.) Source
Description: Casting To and From void*
In C++, pointers are strongly-typed. Therefore, you can't assign a void pointer to any other
pointer type without casting it explicitly:

int n;
void *p = &n;
int *pi=static_cast< int* >(p);//C++ requires explicit cast

In C, the explicit typecasting is not required. By contrast, you can assign any data pointer to
void* without casting:

string s;
void *p=&s; // no cast required here
Danny Kalev
7.) Source-
Description: C++ Null Pointers and Delete
C++ guarantees that operator delete checks its argument for null-ness. If the argument is 0, the
delete expression has no effect. In other words, deleting a null pointer is a safe (yet useless)
operation. There is no need to check the pointer for null-ness before passing it to delete:

if (p) // useless; delete already checks for a null value
Danny Kalev
8.) Source -
Description: Invoking a Function Through a Pointer
When you call a function through a pointer to function, use that pointer as if it were the function
itself, not a pointer. For example:

#include <cstring>
char buff[10];
void (*pf) (char *, const char *);
pf = strcpy; // take address of strcpy
pf(buff, "hi"); // use pf as if it were the function itself
Danny Kalev
9.) Source -
Description: Checking If a Function Pointer is Valid
I am collecting a number of function pointers in C++ and before I call them I want to check to
see if they have been set. I really want to avoid calling them if they aren't valid. I have tried the
following "if func != Null { ... " but I get an error.
First of all, there's no 'Null' in C++. The constant NULL is all uppercase. However, it's not a
keyword so you have to #include a header file to make its definition visible to the compiler. The
best way check for nullness is to compare the pointer (any type of pointer, including a pointer to
member) to the literal zero:
if (pmf!=0)
DevX Pro
10.) Source -
Description: Assigning a Specified Memory Address to a Pointer
In low-level programming and hardware interfaces, you often need to assign a pointer to a
specific physical address. To do that, you have to cast the address value using the
reinterpret_cast operator. Here's an example that shows how this is done:

void *p;
// assign address 0x5800FF to p
p = reinterpret_cast< void* > (0x5800FF);
Danny Kalev
11.) Source -
Description: OS System Calls
How can I pass a member function of a class as a pointer in C (pointer type expected: void *)?
You can't pass a pointer to a C++ class member as void *. A pointer to member is not really a
pointer, it's a data structure. One way to get around this is to declare the member function static.
A pointer to a static member function actually is a pointer to an ordinary function.
For example:
caller(void * a_ptr); //your caller func
typedef void * (*PF)();

class A
static void* func(); //callback member function

int main()
PF pf;
pf = &A::func; // pf is a pointer to function

caller(void * a_ptr)
PF ptr = (PF) a_ptr;
void * val = ptr();//call member function
DevX Pro
12.) Source -
Description: - Assign Zero or NULL to a Pointer
To initialize a pointer after deleting it, should I assign a NULL to it or zero? Are they the same in
C++? I know strictly speaking, I should assign NULLs to deleted pointers. But the app I work
with uses zeros instead. I wonder if I need to change them.
C and C++ define NULL differently. A typical definition of NULL in C++ looks like this:
#define NULL 0
Whereas in C, NULL is usually defined like this:
#define NULL ((void*)0)
Why is this? Pointers in C++ are strongly-typed, unlike pointers in C. Thus, void* cannot be
implicitly converted to any other pointer type without a cast operation. If C++ retained C's
convention, a C++ statement such as:
char * p = NULL;
would be expanded into:
/*error: incompatible pointer types*/
char * p = (void*) 0;
For this reason, the C++ standard requires that NULL be defined either as 0 or as 0L. In other
words, NULL is just an alias for the literal zero. Therefore, you may use zero as a pointer
initializer directly. There's no need to replace 0 with NULL.
DevX Pro
13.) Source -
Description: - Pointer to Structure
I have a struct:
struct mystruct
DWORD dw_Id;
In the following function I want to pass the address of 'dw_Id' which is a member of mystruct.
How do I pass it?
If I am passing as-
foo(pMystruct->(&dw_Id))//compiler error
You pass the address of a pMyStruct member like this:
foo( &pMystruct->dw_Id);
Make sure that pMyStruct points to an instance of Mystruct.
DevX Pro
14.) Source:
Description: Iterators Aren't Pointers
Suppose you define a the following list object and you need the address of one of its elements:

std::list <int> li;
std::list <int>::iterator iter = li.begin();
In many contexts, iter functions like a pointer. However, when you need a real pointer to a
container's element, you can't use an iterator:

int func(int * p);
int main()
func(iter); // error, iter is not a pointer to int
The problem is that in general, iterators aren't pointers. Rather, they are usually implemented as
objects. To get a pointer to the element an iterator refers to, you need to "dereference" that
iterator and take the address of the result. For example:

int main()
func( &*iter); // ok
Danny Kalev
15.) Source -
Description: Pointer Arithmetic
I am a programmer beginning to learn C++. I wrote this chunk of code:
// Get to do some pointer arithmetic!


const char NL = '\n';
const char TB = '\t';
char* ReverseString(char *pStr);

void main()
char *str = "This is a wild test string...";
char *ch = &str[0];

for (unsigned i=0;i<< *ch++;

cout << NL << ReverseString(str);

char *ReverseString(char *pStr)
int len = strlen(pStr);
char *f = &(pStr[0]);
char *b = &(pStr[len-1]);
char temp;

// swap from forward to backward
temp = *f;
*f = *b;
*b = temp;

}while (f < b);

return pStr;

//////////// End of Code //////////////////////////////////
This code compiles without any error warnings. However, when I execute the code, the program
crashes with an Access Violation error. In debugging I found that the offending line is in the
ReverseString function, and I encounter the Access Violation error at the following statement:
*f = *b;
I compiled the same program in C++ Builder 3.0. The program compiled successfully, and on
execution it worked perfectly fine. So, the problem lies with Visual C++ 6.0 compiler.
Let me provide the system configuration.
Platform: Intel
Operating System: Windows 2000 RC2
Development Environment: Visual C++ 6.0 Enterprise Edition SP3
Why is this error happening?
Your reverse() function attempts to modify a const array of chars, which is a disallowed
operation. Remember that every string literal is a constant, and so is str in this statement:
char *str = "This is a wild test string...";
First, never use plain char * when pointing to a literal string. Use const char * instead:
const char *str = "This is a wild test string...";
Now if you try to compile the const version, your compiler will flag the reverse() call in main()
as an error. What you should do is use an array of char rather than a pointer:
char str[] = "This is a wild test string...";
This should be OK.
DevX Pro
16.) Source -
Description:- Possibility of Decompiling an EXE File?
How can I get the source code from EXE files? Does it matter if they are read-only?
This question is asked very frequently. However, the answer is always the same: there's no way
you can decompile an executable and restore the original source file.
The longer answer will also convince you why trying to find such a tool is at least as futile as
trying to find the Holy Grail.
First, remember that there is no 1:1 relation between a piece of assembly code and a
corresponding source code written in a high-level language such as C++. For example, for loops,
switch statements, and while blocks, are all translated into the same assembly directives. In other
words, there is an infinite number of C++ programs that can produce the exact assembly code.
The same is true for other language constructs, which are translated into a single assembly entity:
pointers, references, and arrays, for example.
Furthermore, each compiler produces a different executable for a given source file. Even if you
know the exact compiler brand that was used for compiling the program, as well as its version
number, how can you tell which compile-time options were used during compilation?
Also, how can a decompiler reconstitute macros, inline functions, and typedef's when all these
are all substituted before the compiler actually translates the source code into machine code?
Remember also that normally the debugging information is removed from the executable so that
the decompiler will not be able to reconstitute the original names of variables, classes, arrays,
pointers, functions, and constants.
To conclude, developing a utility that decompiles an executable file into its original C++ source
code (or anything that comes close to the original) is unrealistic.
That also answers the second questionit doesn't really matter whether the files are read-only;
it's simply impossible.
DevX Pro
17.) Source -
Description - Constant Pointer
Can you expalin the difference between the following statements in some examples and when do
we need them:

const char *ptr; //
char *const ptr; //
The following statement:

const char *ptr;
defines a pointer to a const char array. This means that the pointer itself can change its value
(e.g., you can do something like ptr++ or ptr--, as well as assign a new value to ptr). However,
you cannot change the characters in the array to which ptr points. Thus ptr[0] = 'a' is illegal. On
the other hand,

char *const ptr;
declares a const pinter to a non const char array. You can change the characters in the array
through the const pointer: ptr[0] = 'a';//OK but you cannot change the pointer itself: ptr++, ptr--
and assigning a new value to ptr are all illegal.
You need the first form to disable undesirbale changes to the string, such as when you pass it to
another function that is not allowed to modify it.
The second form is rarer. It is used when the pointer is mapped to a hardware port, for example.
DevX Pro
18.) Source -
Description Passing More Arguments to a Callback Function
Callback functions have a fixed signature so you cannot alter the number or type of the arguments it
takes. For example, the standard qsort() function takes a pointer to a function that has the following

int (cmp*)(const void *, const void *) //user's comparison function
This signature allows you to pass exactly two arguments to your custom-made comparison
function. However, suppose that the comparison function has to compare strings and you want to
pass a third argument to indicate whether the comparison should be case-sensitive or not. You
cannot simply add a third argument to the function call, because the compiler will complain
about it (don't even think about casting the function pointer; see this tip). Instead, you can define
a struct that contains two members:

struct Comparison
char *name;
bool caseSensitive;
Now instead of passing a pointer to char as the second argument, you can pass a pointer to an
instance of that struct. The comparison function will unpack the struct and compare the strings

int MyCompareFunc(const void *pfirst, const void * psecond)
const Comparison * pcmp = (Comparison *) psecond;
const char * pstr1 = (const char *) pfirst;
const char * pstr2 = pcmp->name; //extract string from struct
if (pcmp->caseSensitive == true) //the second field of the struct
return strcmp(pstr1, pstr2)
//perform case-insensitive comparison
Danny Kalev
19.) Source -
Description Functions Returning Arrays
Is it possible for a function to return an array instead of a single value?
No but, then again, you don't really pass arrays as parameters, either. What C/C++ can do is pass
the address of an array (a pointer) and you can return a pointer, too. Just be careful that when you
return a pointer to an array, that the array is properly allocated.
For example, if you create an array within your function:

int *MyFunc()
int a[20];
return a;
this will fail because local variables are allocated on the stack and will no longer be valid when
the function returns.
DevX Pro
20.) Source -
Description Member Alignment
Manual calculation of a struct/class size is not just an indication of poor design and a source of
maintenance problems; it may also lead to bugs that are very hard to detect:

struct Person{
char firstName[5];
char lastName[8]
}; //the actual size of Person is most likely larger than 13 bytes
Person p= {{"john"}, {"lippman"}};
memset(&p, 0, 5+8 /* surprise */ ); //not all members of p are completely
Simple addition of the sizes of each data member in a struct/class will probably yield a wrong
size. Wrong sizes occur because the compiler is allowed to add additional padding bytes between
members whose size does not fit exactly into a machine word. Padding enables programs to
execute significantly faster (especially on RISC machines). On a 32 bit (four bytes) processor,
three additional bytes will be inserted between the two members of Person, thus making the first
member occupy eight bytes--a size that fits neatly into two processor's words. In our case, it also
means that Person's size is 16 bytes and not 13. (You should consult your compiler's manual in
order to find out what its default alignment is). Consequently, on some implementations, the
above memset function will leave the last three bytes of p.lastName uninitialized. Therefore, the
size of a struct/class should always be calculated by the sizeof operator:

memset(&p, 0, sizeof(Person)); //correct
Danny Kalev
21.) Source -
Description Pre-Defined Macros
All C/C++ compilers define the following macros:

__DATE__ //a literal containing the compilation date in the form "Apr 13
__TIME__ //a literal containing the compilation time in the form "10:01:07"
__FILE__ //a literal containing the name of the source file being compiled
__LINE__ //a decimal integer containing the current line number in the source
In addition, a C++ compiler defines the following macro:

__cplusplus //useful for combining legacy C code with C++
And an ANSI conforming C compiler defines the following macro:

__STDC__ //useful when one has to test whether the current compiler is ANSI C
These macros can be useful for debugging and issuing diagnostic messages:

if(isSuccessful == false)
printf("failed to open transaction; error occurred at line %d in file %s",
Danny Kalev
22.) Source -
Description When to Pass Parameters by Value
The main reason to pass parameters by value is to improve performance, which is somewhat of a
paradox, because this is also one of the main reasons to pass by reference.
The truth is that either method can improve performanceit just depends on the situation.
Passing a parameter by reference avoids copying all the data members onto the stack, which
could take a lot of resources if you've got a lot of members or you're going to be calling the
recipient function frequently. It also saves the time and memory required to allocated pointer
members; allocating on the heap is much slower than pushing onto the heap.
If the parameter being passed is small, it's possible that the compiler will place the entire value in
a register for its whole lifetime. This is especially the case for built-in types or classes that
contain one or two members that are also built-in types.
If you pass a register variable by value, depending on the processor and the compiler, the
parameter may be passed in a register, which is very fast. If you pass it by reference, the
compiler will have to store the object in RAM so that its address may be takenreferences are
usually implemented as RAM addresses. Essentially they are pointers that are automatically
dereferenced for you, although I don't think the ISO standard requires they be implemented in
any particular way.
Suppose an object could have been stored in a register, and you pass it as a reference or pointer.
This causes the compiler to copy the register to the stack, pass the stack address, and then
possibly copy it back into a register after the call. This definitely slows you down.
Again, this is only permissible if slicing is not a problem. The recipient function expects the
exact same type as the type you are passingnot its base class. This is often the case for simple
classes like geometric points or numerical values.
If the copy constructor for the class you are passing is inlined, it may be faster to pass it by value.
Wael Salman
23.) Source -
Description - How to Choose the Right Data Structure
Following are some tips for matching the most commonly used data structures with particular
1. When to use a Hashtable?
A hashtable, or similar data structures, are good candidates if the stored data is to be accessed in
the form of key-value pairs. For instance, if you were fetching the name of an employee, the
result can be returned in the form of a hashtable as a (name, value) pair. However, if you were to
return names of multiple employees, returning a hashtable directly would not be a good idea.
Remember that the keys have to be unique or your previous value(s) will get overwritten.
2. When to use a List or Vector?
This is a good option when you desire sequential or even random access. Also, if data size is
unknown initially, and/or is going to grow dynamically, it would be appropriate to use a List or
Vector. For instance, to store the results of a JDBC ResultSet, you can use the
java.util.LinkedList. Whereas, if you are looking for a resizable array, use the java.util.ArrayList
3. When to use Arrays?
Never underestimate arrays. Most of the time, when we have to use a list of objects, we tend to
think about using vectors or lists. However, if the size of collection is already known and is not
going to change, an array can be considered as the potential data structure. It's faster to access
elements of an array than a vector or a list. That's obvious, because all you need is an index.
There's no overhead of an additional get method call.
Sometimes, it may be best to use a combination of the above approaches. For example, you
could use a list of hashtables to suit a particular need.
4. Set Classes

And from JDK 1.2 onwards, you also have set classes like java.util.TreeSet, which is useful for
sorted sets that do not have duplicates. One of the best things about these classes is they all abide
by certain interface so that you don't really have to worry about the specifics. For e.g., take a
look at the following code.

// ...
List list = new ArrayList();
Shantanu Garg
24.) Source -
Description Don't Use const for Passing Parameters by Value
To avoid undesirable changes to an argument passed by reference, you pass it as a const
parameter. For example:

void f(const string & s); // f can't change s
However, some programmers use const even when they pass parameters by value, for example:

void f(const int n); // n is passed by value, why const?
Is it really necessary? No, it's not. Remember that when you pass a parameter by value, the
function called can't make changes to original value anyway because it receives a copy of it.
Therefore, the use of const for passing parameters by value only blocks the function from
changing its local copy. However, whether a function changes its local copy of some variable is
an implementation detail; it's not a part of the interface. Therefore, users of this function don't
(and shouldn't) need to know that. As a rule, don't declare parameters const if they are passed or
returned by value.
Danny Kalev
25.) Source -
Description Identifying Memory Leaks in Linux for C++ Programs
Most C++ programmers agree that it can be harrowing trying to identify the memory leaks in a
given program.
If you're working on the GNU/Linux platform, there's an interesting tool you can use to
minimize the hassle of this task: mtrace.
Here's some background on mtrace:
1. You call the mtrace() function to log all memory leaks. The memory allocations and
deallocations are logged to a text file pointed to by the environment variable
2. A Perl utility called mtrace parses the text file logged by your program and identifies the
memory leaks.
The following code allocates memory, but does not essentially free it:

#include <stdlib.h>

int main() {
int *a;

a = malloc(sizeof(int)); //Allocate memory

*a = 7;
//Notice that we are not freeing memory before we end the program.

Now, see how to use mtrace to identify the memory leak:
Step 1: Setup MALLOC_TRACE environment variable to point to a file where mtrace
needs to log the memory allocations:

setenv MALLOC_TRACE /home/karthik/temp/trace.txt
Step 2: Insert mtrace hooks into the program:

#include <stdlib.h>
#include <mcheck.h> /* Header file to include mtrace related functions

int main() {
int *a;

mtrace(); /* This starts memory tracing.
This has to be done before we do a 'malloc' or we allocate memory. */

a = malloc(sizeof(int)); /* Allocate memory */

*a = 7;
/* Notice that we are not freeing memory before we end the program.

Step 3: Compile the modified program with the debugging options turned on:

$ gcc -g -Wall -ansi -pedantic leak.c
Step 4: Run the program.
Step 5: Use the mtrace utility to retrieve the information. Here's what the syntax looks

mtrace <exec_file_name> <malloc_trace_filename>

[akkumar@empress work]$ mtrace a.out ~/temp/trace.txt

Memory not freed:
Address Size Caller
0x08049910 0x4 at /home/karthik/tips/leak.c:9
This precisely tells you that there is a potential memory leak at line 9:

a = malloc(sizeof(int)); /* Allocate memory */
mtrace is a GNU utility.
The code in this tip was tested on a Linux platform with the gcc 3.2.3.
Karthik Kumar Arun Kumar
26.) Source -
Description - When Is It Safe to Use Inline?
Excessive use of inline functions might bloat the size of the executable and reduce execution
speed. In other words, using inline abundantly can degrade rather than enhance performance. For
this reason, many programmers avoid function inlining altogether because they can't predict how
inlining will affect performance. However, there is one guarantee: very short functions (e.g., a
function that merely calls another function or a function that returns a data member of its object)
are always good candidates for inlining. For example:

inline bool Foo::is_connection_alive()
return ::connection_alive(); // calls an API function
inline int Bar::get_x()
return x; // merely returns a data member
By inlining such functions, the size of the executable decreases slightly and execution speed is
boosted. Note, however, that this rule applies to very short functions exclusively. For larger
functions, you usually need to profile the software with and without inline to assess the effects of
Danny Kalev