Wednesday, June 29, 2022

Stack overflow on Linux

...exceeding the stack limit is usually considered a segmentation violation, and systems with enough memory management to detect it will send a SIGSEGV [segmentation fault] when it happens.

A typical symptom in a C++ program running on Linux is getting a segmentation fault when entering an innocent function like pow(). To debug, decrease stack usage (e.g. if there is a static array, decrease its size) in the code before the segfault, run your program in debug mode, see if your program continued further than before. Unfortunately, the same program might be working on Windows without problems.

To increase stack size on Linux, use ulimit -s <size_KB>

Thursday, June 23, 2022

Benefits of wiki style documentation

Wiki style (no signatures/approvals) documentation of complex software projects (lines of code > 10K) has the following benefits:

  1. Wiki approach saves you from wasting time in publication and approval process. It enables quick updates which increases quality of content.
  2. You as the developer will be able to remember important details of design, especially if long time has passed since you last worked on it.
  3. You can hand-off the project to junior developers without wasting your time.
  4. You can easily extract a user manual from existing content and users won't bother you with questions.
  5. Code reviewers get a better idea of overall design which increases the quality of review comments.

Wednesday, June 22, 2022

Java: Table with numeric input and length checks

Sample code showing how to do numeric input and character length checks with a JTable:


/*
* Table with numeric input and length checks.
* Şamil Korkmaz, June 2022
*/
package tablewithnumericinput;
import java.awt.Color;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.DefaultCellEditor;
import javax.swing.InputVerifier;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.DefaultTableModel;
public class TableWithNumericInput {
static private JTextField jtfForNumericFields;
static class MyKeyListener implements KeyListener {
@Override
public void keyTyped(KeyEvent ke) {
if (ke.getKeyCode() != KeyEvent.VK_BACK_SPACE && ke.getKeyCode() != KeyEvent.VK_DELETE) {
char c = ke.getKeyChar();
System.out.println("c = " + c);
System.out.println("Inside keyTyped()");
if (ke.getSource() instanceof JTextField) { //When user first double clicks on table and then presses a key, jtf will be the key event source
System.out.println("Key event source is text field");
} else if (ke.getSource() instanceof JTable) { //When table has focus but user has not double clicked on it, table will be the key event source
System.out.println("Key event source is table");
}
try {
Double.parseDouble(jtfForNumericFields.getText() + c); //getText() returns text before keypress, we have to add pressed key char to get full text
if (c == 'd' || c == 'f') { //parseDouble accepts 'd' and 'f' at the end of text
ke.consume(); //ignore key press
} else {
if (!jtfForNumericFields.getInputVerifier().verify(jtfForNumericFields)) { //check if text exceeds max nb of characters
ke.consume(); //ignore key press
}
}
} catch (NumberFormatException e) { //text is not a number
ke.consume(); //ignore key press
}
}
}
@Override
public void keyPressed(KeyEvent ke) {
}
@Override
public void keyReleased(KeyEvent ke) {
}
}
static class MyTable extends JTable {
public MyTable() {
putClientProperty("terminateEditOnFocusLost", true); //To make sure that setValue() is called when user clicks on another component, e.g. a button.
}
@Override
public void setValueAt(Object value, int iRow, int iColumn) {
if (value != null && ((String)value).length() > 0) {
if (iColumn > 0) {
double doubleValue = Double.parseDouble((String)value);
super.setValueAt(doubleValue, iRow, iColumn);
} else {
super.setValueAt(value, iRow, iColumn);
}
}
}
}
public static void main(String[] args) {
JFrame jf = new JFrame();
jf.setTitle("Table with numeric input checks");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.getContentPane().setLayout(new javax.swing.BoxLayout(jf.getContentPane(), javax.swing.BoxLayout.PAGE_AXIS));
JTable jt = new MyTable();
jf.add(new JScrollPane(jt)); //to show table column headers
JButton jb = new JButton("OK");
jb.addActionListener((java.awt.event.ActionEvent evt) -> {
JOptionPane.showMessageDialog(jf, "Table contents: " + jt.getValueAt(0, 0) + ", " + jt.getValueAt(0, 1));
});
jf.add(jb);
jf.setBounds(100, 100, 400, 150);
jt.setBorder(javax.swing.BorderFactory.createLineBorder(Color.BLACK));
DefaultTableModel model = new DefaultTableModel() {
Class[] types = new Class[]{String.class, Double.class};
@Override
public Class getColumnClass(int iCol) {
return types[iCol];
}
};
jt.setModel(model);
model.addColumn("ID");
model.addColumn("Double Value");
model.addRow(new Object[]{});
final int PANEL_WIDTH = 200;
final int TABLE_HEIGHT = 3 * jt.getRowHeight();
jt.setBounds(0, jt.getRowHeight(), PANEL_WIDTH, TABLE_HEIGHT);
jt.getTableHeader().setBounds(0, 0, PANEL_WIDTH, jt.getRowHeight());
jtfForNumericFields = new JTextField();
jtfForNumericFields.setInputVerifier(new InputVerifier() {
@Override
public boolean verify(JComponent jc) {
JTextField jtf = (JTextField) jc;
String text = jtf.getText();
System.out.println("Text: " + text);
final int maxLength = 9;
if (text.length() > (maxLength - 1)) {
System.out.println("length > " + maxLength);
return false;
}
return true;
}
});
//Note that we have to add key listener to both table and text field because when
//table column 1 has focus and user presses a key, editing will start and table will catch
//key events. If user double click on column 1, then text field will catch key events:
jt.addKeyListener(new MyKeyListener());
jtfForNumericFields.addKeyListener(new MyKeyListener());
//Use numeric text field as editor for column 1 (second column):
jt.getColumnModel().getColumn(1).setCellEditor(new DefaultCellEditor(jtfForNumericFields));
jf.setVisible(true);
}
}

Tuesday, June 21, 2022

Stop build when "C4013 ... undefined; assuming extern returning int"

Recently I had to deal with a bug in a C project whose root cause was fabs not functioning properly due to missing #include <math>. Finding the root cause involved diving into more than 4 layers of abstractions, it was not fun (!) When run from Visual Studio as a dll project, the code resulted in fabs(10) >= fabs(20) to be true! When I compiled the project with Visual Studio, it gave "warning C4013: 'fabs' undefined; assuming extern returning int". I would normally expect the build to fail in the linking stage. Probably the linker is able to find the fabs but since it was first assumed to be returning int (4 bytes) and fabs returns double (8 bytes), this will result in 4 bytes of the return value being ignored, which can cause funny values. For more about the reason why this behavior is allowed, see my similar trouble with malloc before.

Another interesting fact is that the code was working properly when run from a Simulink s-function. I assume Simulink includes math.h somewhere in its hierarchy. Here is the simplified code:

//When the #include <math.h> line below is commented out, code will build with "warning C4013: 'fabs' undefined; assuming extern returning int" message.
//It will also run although fabs is not defined. In that case, fabs() will always the same number, it can be anything, e.g. 8, 256.
//This will cause the fabs(10) >= fabs(20) line to be true.
//In Visual Studio, to prevent build when there is a 4013 warning, go to project Properties - C/C++ - Advanced - Treat Specific Warnings as Errors - Add 4013
//Şamil Korkmaz, June 2022
#include <stdio.h>
//#include <math.h>
int main() {
fabs(10) >= fabs(20) ? printf("true\n") : printf("false\n");
double a = fabs(10);
double b = fabs(20);
double c;
printf("a = %1.1f, b = %1.1f\n", a, b);
printf("Press enter...\n");
getchar();
}
view raw fabsProblem.c hosted with ❤ by GitHub

To prevent Visual Studio from building when 4013 warning exists, go to project Properties - C/C++ - Advanced - Treat Specific Warnings as Errors - Add 4013:



Wednesday, June 15, 2022

String input to Matlab C Mex file

When you call a Matlab C Mex function with a string input, make sure to use single quotes, e.g. 'hello'. If you use double quotes, e.g. "hello", Matlab will crash and burn!